<?xml version="1.0" encoding="utf-8"?>
<!-- generator="FeedCreator 1.7.2-ppt DokuWiki" -->
<?xml-stylesheet href="http://www.pic24.ru/lib/exe/css.php?s=feed" type="text/css"?>
<rdf:RDF
    xmlns="http://purl.org/rss/1.0/"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
    xmlns:dc="http://purl.org/dc/elements/1.1/">
    <channel rdf:about="http://www.pic24.ru/feed.php">
        <title>PIC24 osa:articles</title>
        <description></description>
        <link>http://www.pic24.ru/</link>
        <image rdf:resource="http://www.pic24.ru/lib/images/favicon.ico" />
       <dc:date>2023-02-11T04:09:38+03:00</dc:date>
        <items>
            <rdf:Seq>
                <rdf:li rdf:resource="http://www.pic24.ru/doku.php/osa/articles/encoding_without_errors?rev=1276366821"/>
                <rdf:li rdf:resource="http://www.pic24.ru/doku.php/osa/articles/intro?rev=1365513233"/>
                <rdf:li rdf:resource="http://www.pic24.ru/doku.php/osa/articles/modules?rev=1279523651"/>
                <rdf:li rdf:resource="http://www.pic24.ru/doku.php/osa/articles/mpasm_formatting?rev=1303575275"/>
                <rdf:li rdf:resource="http://www.pic24.ru/doku.php/osa/articles/pk2_osa_lights?rev=1295426368"/>
                <rdf:li rdf:resource="http://www.pic24.ru/doku.php/osa/articles/pk2_osa_piano?rev=1275227741"/>
                <rdf:li rdf:resource="http://www.pic24.ru/doku.php/osa/articles/rtos_misbeliefs?rev=1305360719"/>
                <rdf:li rdf:resource="http://www.pic24.ru/doku.php/osa/articles/rtos_usage?rev=1291812251"/>
                <rdf:li rdf:resource="http://www.pic24.ru/doku.php/osa/articles/rtos_usage_1?rev=1266955627"/>
                <rdf:li rdf:resource="http://www.pic24.ru/doku.php/osa/articles/rtos_usage_2?rev=1266955684"/>
                <rdf:li rdf:resource="http://www.pic24.ru/doku.php/osa/articles/rtos_usage_3?rev=1266955756"/>
                <rdf:li rdf:resource="http://www.pic24.ru/doku.php/osa/articles/rtos_usage_4?rev=1266955996"/>
                <rdf:li rdf:resource="http://www.pic24.ru/doku.php/osa/articles/scl?rev=1275025460"/>
                <rdf:li rdf:resource="http://www.pic24.ru/doku.php/osa/articles/vga_game?rev=1448561546"/>
                <rdf:li rdf:resource="http://www.pic24.ru/doku.php/osa/articles/vga_terminal?rev=1308416534"/>
                <rdf:li rdf:resource="http://www.pic24.ru/doku.php/osa/articles/volatile_for_chainiks?rev=1675385653"/>
                <rdf:li rdf:resource="http://www.pic24.ru/doku.php/osa/articles/wdt?rev=1378319751"/>
                <rdf:li rdf:resource="http://www.pic24.ru/doku.php/osa/articles/winavr_bug?rev=1276665351"/>
            </rdf:Seq>
        </items>
    </channel>
    <image rdf:about="http://www.pic24.ru/lib/images/favicon.ico">
        <title>PIC24</title>
        <link>http://www.pic24.ru/</link>
        <url>http://www.pic24.ru/lib/images/favicon.ico</url>
    </image>
    <item rdf:about="http://www.pic24.ru/doku.php/osa/articles/encoding_without_errors?rev=1276366821">
        <dc:format>text/html</dc:format>
        <dc:date>2010-06-12T22:20:21+03:00</dc:date>
        <title>Как писать программы без ошибок</title>
        <link>http://www.pic24.ru/doku.php/osa/articles/encoding_without_errors?rev=1276366821</link>
        <description>
&lt;p&gt;

Виктор Тимофеев, &lt;a href=&quot;mailto:&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot; class=&quot;mail JSnocheck&quot; title=&quot;&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot;&gt;osa@pic24.ru&lt;/a&gt; (ноябрь, 2009)
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/encodingwithouterrors.pdf&quot; class=&quot;media mediafile mf_pdf&quot; title=&quot;osa:articles:encodingwithouterrors.pdf&quot;&gt;Скачать в PDF-формате&lt;/a&gt;
&lt;/p&gt;



&lt;h1&gt;&lt;a name=&quot;как_писать_программы_без_ошибок&quot; id=&quot;как_писать_программы_без_ошибок&quot;&gt;Как писать программы без ошибок&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Как писать программы без ошибок&quot; [154-230] --&gt;
&lt;h1&gt;&lt;a name=&quot;введение&quot; id=&quot;введение&quot;&gt;Введение&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Введение&quot; [231-262] --&gt;
&lt;h3&gt;&lt;a name=&quot;про_ошибки&quot; id=&quot;про_ошибки&quot;&gt;Про ошибки&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;p&gt;
По профессии я занимаюсь поиском и исправлением ошибок в чужих программах. За время работы я набрал некоторую коллекцию всевозможных багов и попытался свести их в одну таблицу и классифицировать с тем, чтобы оформить некую брошюру по быстрому поиску и исправлению наиболее часто встречающихся ошибок. Однако произвести такую классификацию не удалось. Дело в том, что, выделив 5-6 основных ошибок, таких как: неправильное приведение типов, путаница со знаковыми и беззнаковыми выражениями, пренебрежение выполнением проверок аргументов функций и пр., - я увидел, что остальные ошибки слишком индивидуальны, чтобы их как-то обобщать. О приемах поиска и говорить нечего, т.к. их намного больше, чем самих ошибок. 
&lt;/p&gt;

&lt;p&gt;
Тем не менее, каждый раз, исправляя какую-либо ошибку, я для себя отмечал, что ее можно было и не совершить, если бы то-то, то-то. Со временем я уяснил, что большая часть ошибок совершается из-за неправильного подхода к процессу программирования. Поэтому идея написания брошюры «Локализация и исправление ошибок» перешла в идею написания брошюры «Как не совершать ошибки». И я считаю это правильным, потому что лучше учиться строить, а не восстанавливать плохо построенное. Большая часть советов, описываемых мной, довольно банальны и просты. Вроде как, все их знают, но почему-то многие не придерживаются. 
&lt;/p&gt;

&lt;p&gt;
О каких ошибках идет речь. Конечно же, не о тех, которые обнаруживает компилятор при трансляции программы. Пока мы не исправим эти ошибки, нам не удастся получить код, который мы сможем прошить в контроллер. Эти ошибки в большинстве своем означают, что текст не соответствует правилам языка программирования, и, по сути, являются помарками. Также в этом материале не хотелось бы касаться так называемых «запланированных» ошибок (обработка неправильных данных, пришедших извне, ошибочные действия пользователя и пр.). Мы будем говорить об ошибках реализации, т. е. о тех, которые будут проявляться в ходе выполнения программы. Эти ошибки являются следствием неправильно запрограммированного (или составленного) алгоритма, допуска условностей, невнимательности и неаккуратности. 
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Про ошибки&quot; [263-4121] --&gt;
&lt;h3&gt;&lt;a name=&quot;для_кого_это_пособие&quot; id=&quot;для_кого_это_пособие&quot;&gt;Для кого это пособие&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;br/&gt;
 
&lt;p&gt;

В этом пособии я изложил свой подход к программированию микроконтроллеров, привел свои правила, которыми руководствуюсь при написании программ, и обозначил те моменты, которым при написании программ уделяю особое внимание. Уверен, что моя точка зрения не единственная, а также, что где-то мне самому еще есть чему поучиться. Так что я буду рад, не только если пособие окажется кому-то полезным, но также если кто-то решит меня в чем-то поправить или дополнить.
&lt;/p&gt;

&lt;p&gt;
Статья рассчитана на широкий круг программистов, поэтому я старался не затрагивать концепцию доказательного программирования, и вообще старался привести больше практических советов, чем теории (с которой сам на «Вы») и формалистики. Так же в этом пособии нет описания приемов повышения надежности программ (самодиагностика, троирование, перепроверка действий и пр.). 
&lt;/p&gt;

&lt;p&gt;
Не следует считать, что после прочтения данного пособия ваши программы автоматически станут безошибочными, и уж тем более, что в будущем ошибок не будет вовсе. Ошибки все равно будут, но процент их сильно сократится. Более того, на написание программ по правилам, изложенным в этом пособии, будет затрачено время в два, в три, в пять раз больше, чем без правил. Писать программы без ошибок - это труд, причем кропотливый. Просто в отличие от кропотливого труда по поиску и исправлению ошибок, этот вид труда поддается планированию и формализации, т.е. является предсказуемым. А применение описанных здесь правил в разы сократит время непредсказуемого процесса отладки, который, как показывает практика, зачастую отнимает намного больше времени, чем само кодирование.
&lt;/p&gt;

&lt;p&gt;
Предполагается, что читатель знает, что такое контроллер, как он устроен, как включается, наконец, что такое электрический ток, потому что без этих знаний заниматься микроконтроллерами бесполезно. Так что, как говорилось в старом анекдоте: «Учите матчасть!»
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Для кого это пособие&quot; [4122-7572] --&gt;
&lt;h2&gt;&lt;a name=&quot;учите_матчасть&quot; id=&quot;учите_матчасть&quot;&gt;«Учите матчасть!»&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Это самая очевидная рекомендация. Про нее можно было и не говорить, но для полноты картины вскользь коснемся этой темы. Программист, пишущий для контроллеров должен знать схемотехнику, устройство самого контроллера, язык программирования, на котором пишет программу, и особенности используемого компилятора. Тот программист, который пренебрегает необходимостью иметь эти знания, теряет право жаловаться на то, что его программа не работает.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;схемотехника&quot; id=&quot;схемотехника&quot;&gt;Схемотехника&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Как минимум нужно знать:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; основы электроники (и цифровой и аналоговой)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; законы Ома и Кирхгофа&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; типовые схематические решения (RC-цепочки, транзисторные ключи)&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Кроме того, нужно знать, что необходимо устанавливать диоды параллельно катушкам реле, что светодиоды включаются через токоограничивающий резистор, что у механических контактов есть такое явление как дребезг, и пр.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;контроллер&quot; id=&quot;контроллер&quot;&gt;Контроллер&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Нужно знать архитектуру контроллера, способы адресации, регистры и их назначения, периферийные модули и режимы их работы, диапазоны рабочих температур, частот, питаний и пр., нагрузочная способность портов и т.д.
&lt;/p&gt;

&lt;p&gt;
Не нужно стесняться заглядывать в фирменные даташиты, там есть ответы на большинство вопросов. А то у кого-то не работает PORTA (в то время как не отключены аналоговые цепи), у кого-то не появляется «1» на выходе RA4, у кого-то прерывание по RB0 срабатывает только в половине случаев и т.д.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;язык&quot; id=&quot;язык&quot;&gt;Язык&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Конечно же, нужно знать сам применяемый язык программирования. 
&lt;/p&gt;

&lt;p&gt;
Для ассемблера это:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; формат команд;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; типы и количество операндов&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Для языков высокого уровня это:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; семантика операторов;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; квалификаторы данных и функций;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; типы данных;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; преобразование типов;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; приоритеты операций;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; указатели (или указатели на указатели);&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;компилятор&quot; id=&quot;компилятор&quot;&gt;Компилятор&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Разработчики компиляторов для микроконтроллеров, стремясь адаптировать компилятор под конкретную платформу, позволяют себе отходить от стандартов языка, одновременно не нарушая их. Каждый компилятор имеет свои особенности, незнание которых может привести к трудностям при портировании или при использовании чужих библиотек: 
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; набор директив;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; типы данных (размерность, знаковость);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; квалификаторы (near, far, const, rom);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; организация прерываний.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;«Учите матчасть!»&quot; [7573-11567] --&gt;
&lt;h2&gt;&lt;a name=&quot;этапы_программирования&quot; id=&quot;этапы_программирования&quot;&gt;Этапы программирования&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Когда ТЗ согласовано и задача формализована (переведена с проблемно ориентированных требований в технические: входные/выходные данные, режимы работы и пр.), начинается сам процесс программирования.

&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;&lt;a href=&quot;#планирование_программы&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Планирование&lt;/a&gt;&lt;/strong&gt; (включает в себя проектирование, составление плана действий, выявление требуемых ресурсов).&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;&lt;a href=&quot;#написание_программы&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Кодирование&lt;/a&gt;&lt;/strong&gt; (запись самой программы в машинном языке)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;&lt;a href=&quot;#отладка_и_тестирование&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Отладка&lt;/a&gt;&lt;/strong&gt; (локализация и устранение ошибок)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;&lt;a href=&quot;#отладка_и_тестирование&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Тестирование&lt;/a&gt;&lt;/strong&gt; (проверка работоспособности и соответствие спецификации)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;span style='color:grey; '&gt;Оформление проектной документации&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;span style='color:grey; '&gt;Эксплуатация и сопровождение&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Беда в том, что многие пренебрегают планированием, а также считают, что отладка появляется только в том случае, если была допущена какая-то ошибка при кодировании, а так как надеются с первого раза написать без ошибок, то и процесс отладки не учитывается. Однако же, в реальности отладка – это обязательный процесс, на который, вдобавок, приходится большая часть времени, сил и нервов. Закодировать даже самый сложный и запутанный алгоритм – это нетрудно, трудно заставить его работать, или, если они был написан правильно, убедиться в его работоспособности. Поэтому процессы отладки и тестирования самые длительные и самые трудоемкие. Однако, время отладки можно сократить, сделав процесс более предсказуемым и управляемым, а именно – спланировав программу.
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Этапы программирования&quot; [11568-14281] --&gt;
&lt;h1&gt;&lt;a name=&quot;планирование_программы&quot; id=&quot;планирование_программы&quot;&gt;Планирование программы&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
Почему-то часто планированием программы вообще пренебрегают. И в лучшем случае все планирование состоит из подсчета количества требуемых выводов входа/выхода. Напомню одну старую шутку: «Нетщательно спланированная работа отнимает в 3 раза больше предполагаемого времени, тщательно спланированная – только в два». Ее можно дополнить: «Неспланированная работа отнимает все время».
Кто-то может сказать: «Я пишу маленькие программы, что там планировать?». Тем не менее, практика показывает, что лучше для маленькой программы потратить 10-15 минут на планирование (просто расписать на листе бумаги), чем тратить 3-4 дня на поиск ошибки, рытье Интернета и отбивание от обвинений в ламерстве на форумах в сети.
&lt;/p&gt;

&lt;p&gt;
Ниже приведены &lt;span class=&quot;important&quot;&gt;этапы планирования программы&lt;/span&gt;:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#расписать_алгоритм&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Расписать алгоритм&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#продумать_модули&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Продумать модули&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#продумать_данные&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Продумать данные&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#разделить_периферию_контроллера_между_процессами&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Разделить периферию контроллера между процессами&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#учесть_физические_свойства_обвеса&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Учесть физические свойства обвеса&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#предусмотреть_возможность_расширения&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Предусмотреть возможность расширения&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#предусмотреть_смену_платформы_или_компилятора&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Предусмотреть смену платформы или компилятора&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Рассмотрим каждый этап более подробно.
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Планирование программы&quot; [14282-16275] --&gt;
&lt;h2&gt;&lt;a name=&quot;расписать_алгоритм&quot; id=&quot;расписать_алгоритм&quot;&gt;Расписать алгоритм&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Алгоритм – это ДНК программы, вернее – ее генетический код. Если в нем ошибка, то программа будет вести себя неправильно. Часто еще при составлении алгоритма на бумаге всплывают некоторые ответвления, которые могут быть проигнорированы при написании программы «в лоб». Преимущество в том, что алгоритм составляется в абстрактных терминах, еще нет ни типов данных, ни переменных, поэтому есть возможность сосредоточиться на конкретных алгоритмических моментах, не думая пока о деталях реализации. В большинстве случаев требуется составить несколько алгоритмов: один общий для всей программы, описывающий режимы работы и порядок переключения между ними, и алгоритмы работы каждого режима в отдельности. Степень детальности алгоритма, конечно, зависит от сложности программы в целом и каждого узла в отдельности.
&lt;/p&gt;

&lt;p&gt;
Алгоритм может быть представлен как в виде блок-схемы, так и в виде графа переходов, в виде графика эпюр сигналов и т.д. Не стоит забывать о требованиях к проектной документации; например, если в документации требуется привести алгоритм в виде блок-схемы, то не нужно его сначала рисовать в виде графа переходов, а затем переводить в блок-схему.
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Расписать алгоритм&quot; [16276-18453] --&gt;
&lt;h2&gt;&lt;a name=&quot;продумать_модули&quot; id=&quot;продумать_модули&quot;&gt;Продумать модули&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Нам нужно заранее продумать, из каких модулей будет собираться наша программа, вернее, на какие модули она будет разбита. Преимущества модульности я поясню ниже, а здесь приведу рекомендации, как правильно разбить программу на модули.
&lt;/p&gt;

&lt;p&gt;
Во-первых, разбиение должно быть произведено по функциональному признаку. Т.е. не нужно в один модуль заталкивать USART и вывод звука на пьезодинамик, даже если при конкретной реализации это и кажется целесообразным. Дело в том, что однажды написанный модуль можно будет переносить в другие проекты, избавляя себя от необходимости писать один и тот же код по много раз. Чем модуль будет функционально изолированнее, тем проще будет его перенос. Однако и здесь не следует бросаться в крайности и делать отдельно модули для передачи данных по USART, для приема данных по USART, для подсчета и сравнения контрольной суммы данных, принятых по USART, и т.п. Функционально модуль должен быть полным, но безысбыточным.
&lt;/p&gt;

&lt;p&gt;
Также стоит отметить, что при разбиении своей будущей программы на модули нужно учитывать наличие уже готовых модулей (либо своих, либо чужих). 
&lt;/p&gt;

&lt;p&gt;
Большинство модулей можно разделить на два типа: системные (работают на уровне сигналов и  железа) и алгоритмические (работают на уровне обработки данных и режимов). Хороший пример – работа ЖКИ. Предположим, нам требуется выводить информацию на ЖКИ HD44780. Крайне неудачным решением будет такое, при котором функции вывода конкретных данных на экран, вызываемые из головной программы, и функции работы с самим HD44780, вызываемые из функции вывода данных, будут помещены в один модуль. Тут получается, что модули обоих типов – алгоритмический и системный – смешаны в один, что сильно затруднит в дальнейшем, например, использование индикатора другого типа. Если же мы четко разделим системный функционал и алгоритмический, то в дальнейшем замена типа индикатора выльется для нас всего лишь в замену системного модуля.
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Продумать модули&quot; [18454-21968] --&gt;
&lt;h2&gt;&lt;a name=&quot;продумать_данные&quot; id=&quot;продумать_данные&quot;&gt;Продумать данные&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Также на этапе планирования мы определяем для себя, какими данными будет оперировать наша программа. Причем нужно обозначить не только назначение данных и требуемый их объем, но также заранее предусмотреть их размещение (ROM или RAM, конкретный банк RAM) и область видимости (например, нам нет смысла делать видимым во всей программе буфер выходных данных i2c).
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Продумать данные&quot; [21969-22672] --&gt;
&lt;h2&gt;&lt;a name=&quot;разделить_периферию_контроллера_между_процессами&quot; id=&quot;разделить_периферию_контроллера_между_процессами&quot;&gt;Разделить периферию контроллера между процессами&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Периферийные модули контроллера помогают упростить некоторые программные узлы, а иногда просто делают их возможными (например, без АЦП мы не сможем измерить аналоговый сигнал). Но зачастую бывает, что программных узлов, требующих использование встроенной периферии, больше, чем имеется на борту у контроллера. Поэтому периферийные модули приходится разделять между несколькими задачами (типичный пример – таймеры). При проектировании программы нужно заранее распределить периферийные модули между задачами, что поможет выбрать оптимальные параметры для каждого модуля. 
&lt;/p&gt;

&lt;p&gt;
(Видел программу, в которой неправильно распределенные ресурсы привели к тому, что пришлось мультиплексировать управление GSM-модулем и GPS-приемником на один USART. Программа начала сбоить и, насколько я знаю, ее так и не привели в рабочее состояние.)
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Разделить периферию контроллера между процессами&quot; [22673-24294] --&gt;
&lt;h2&gt;&lt;a name=&quot;учесть_физические_свойства_обвеса&quot; id=&quot;учесть_физические_свойства_обвеса&quot;&gt;Учесть физические свойства обвеса&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Контроллер с нашей программой будет жить не сам по себе, его будут окружать какие-то внешние схематические узлы, специализированные микросхемы и пр. Проектируя программу, нужно заранее учесть особенности всех подключенных к контроллеру устройств, цепей и микросхем. 
&lt;/p&gt;

&lt;p&gt;
Например, если в устройстве будут кнопки, то нужно помнить, что механические контакты дребезжат и шуршат. Это сразу должно наталкивать на использование каких-то дополнительных счетчиков и/или таймеров для подавления этих эффектов. 
&lt;/p&gt;

&lt;p&gt;
Также стоит помнить про «холодный» старт внешней периферии. Например, у нас есть ЖКИ, которому после подачи питания требуется 10 мс для самоинициализации, во время которой он не будет слышать команд извне. Если этого не учесть, то может получиться так, что наш контроллер начинает инициализацию ЖКИ примерно в то же время, когда ЖКИ заканчивает свою внутреннюю инициализацию. В результате ЖКИ то будет успевать самоинициализирваться и начинать прием наших данных, то не будет. И когда мы поймем, в чем дело, и увеличим задержку перед инициализацией до 20 мс, мы с удивлением обнаружим, что теперь при включении питания, например, успевает туда-сюда сработать реле, т.к. с новой задержкой неинициализированное состояние управляющего им вывода держится достаточно для того, чтобы ток в катушке реле успел вырасти и привести к срабатыванию. 
&lt;/p&gt;

&lt;p&gt;
Если в нашем устройстве предполагается прием данных по радио, то мы должны учесть, что в радиоканале есть помехи, и что прием такого сигнала нельзя делать «в лоб» по фронтам, а нужно, как и в случае с обработкой сигналов от механических контактов, заводить дополнительные счетчики, таймеры и т.п.
&lt;/p&gt;

&lt;p&gt;
Примеров про обвес можно придумать еще много: состязания на шине, емкостные нагрузки, внешние источники прерываний и т.д. Все это нужно учитывать еще до начала написания текста программы, заранее предусматривая порядок действий и резервируя дополнительные ресурсы (память и скорость).
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Учесть физические свойства обвеса&quot; [24295-27889] --&gt;
&lt;h2&gt;&lt;a name=&quot;предусмотреть_возможность_расширения&quot; id=&quot;предусмотреть_возможность_расширения&quot;&gt;Предусмотреть возможность расширения&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
При проектировании программы следует заранее убедиться, что в случае значительного разрастания в дальнейшем функционала (а значит и кода) будет  возможность заменить выбранный контроллер более мощным с минимальными затратами. Если мы, например, решили использовать в нашей разработке контроллер из линейки PIC16, а на этапе планирования мы прикинули, что подойдет только 16F877, или 16F946, или 16F887 (короче говоря – с максимальным объемом памяти), значит, мы неправильно выбрали линейку. В этом случае нужно брать PIC18, потому что велика вероятность того, что программа в выбранный контроллер просто не влезет.
&lt;/p&gt;

&lt;p&gt;
Часто встречаю на форумах крики души: «помогите оптимизировать программу, а то она не лезет в PIC18F67J60!» (прим.: контроллер из линейки PIC18, имеющий максимально возможный объем ROM = 128Кб). Это результат непродуманного выбора контроллера на этапе планирования, если этот этап вообще был проведен.
&lt;/p&gt;

&lt;p&gt;
Так же надо учесть, что при отладке программы нам потребуются кое-какие ресурсы (об этом речь пойдет ниже).
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Предусмотреть возможность расширения&quot; [27890-29806] --&gt;
&lt;h2&gt;&lt;a name=&quot;предусмотреть_смену_платформы_или_компилятора&quot; id=&quot;предусмотреть_смену_платформы_или_компилятора&quot;&gt;Предусмотреть смену платформы или компилятора&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Здесь речь, конечно, не о том, что нужно напичкать программу директивами условной компиляции, позволяющими максимально эффективно использовать возможности каждого компилятора, на который, возможно, придется переносить программу. Речь как раз о том, чтобы:

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;минимально использовать уникальные особенности конкретного компилятора&lt;/strong&gt;, а те куски кода, которые все-таки так пишутся, блокировать условными директивами (бывает, что компилятор простую операцию развернет черт те во что, а – кровь из носа - хочется сделать кратко и красиво);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;не использовать недокументированные особенности компилятора&lt;/strong&gt;; некоторые операции могут быть реализованы различным способом, например, операция сдвига влево может после своего выполнения оставить флаг переноса, а может и изменит его, либо, выполнив оптимизацию, заменит инструкцию сдвига чем-нибудь, либо в качестве результата возьмет один из промежуточных результатов, получившихся при вычислении предыдущего выражения. В общем, нет гарантии, что флаг переноса будет установлен правильно. &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;переопределять типы данных.&lt;/strong&gt; Стоит учитывать, что знаковость и размерность в стандарте языка определены для каждого типа довольно расплывчато (например, в HT-PICC18 тип char по умолчанию беззнаковый, в то время как в MPLAB C18 – знаковый; или в CCS тип int – 8-битный беззнаковый, а в остальных компиляторах – 16-битный знаковый). Поэтому  в каждом проекте хорошо бы иметь h-файл с переопределениями типов&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;     &lt;span class=&quot;co2&quot;&gt;#if defined(__PICC__) || defined(__PICC18__)&lt;/span&gt;
     &lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;signed&lt;/span&gt;   &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;    S08;
     &lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;signed&lt;/span&gt;   &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;     S16;
     &lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;signed&lt;/span&gt;   &lt;span class=&quot;kw4&quot;&gt;long&lt;/span&gt;    S32;
&amp;nbsp;
     &lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;    U08;
     &lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;     U16;
     &lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;long&lt;/span&gt;    U32;
&amp;nbsp;
     &lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; bit              &lt;span class=&quot;kw4&quot;&gt;BOOL&lt;/span&gt;;
&amp;nbsp;
     &lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;signed&lt;/span&gt;   &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;    S_ALU_INT;
     &lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;    U_ALU_INT;
     &lt;span class=&quot;co2&quot;&gt;#endif&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Обратим внимание на два типа: S_ALU_INT и U_ALU_INT – это знаковое и беззнаковое целые, имеющие размерность машинного слова для конкретного контроллера. Т.к. операции над операндами, имеющими размерность шины данных, производятся наиболее оптимально, иногда есть смысл пользоваться этими типами данных.
&lt;/p&gt;

&lt;p&gt;
&lt;p&gt;&lt;div class=&quot;noteclassic&quot;&gt;
&lt;strong&gt;Примечание&lt;/strong&gt;: далее в тексте для наглядности будут использоваться стандартные типы: char (8 бит), int (16 бит), long (32 бита).

&lt;/div&gt;&lt;/p&gt;
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Предусмотреть смену платформы или компилятора&quot; [29807-33707] --&gt;
&lt;h1&gt;&lt;a name=&quot;написание_программы&quot; id=&quot;написание_программы&quot;&gt;Написание программы&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
При написании самого текста программы нужно руководствоваться двумя сводами правил: 
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#кодирование&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;правила кодирования&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#оформление&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;правила оформления&lt;/a&gt; &lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Сами правила могут у каждого быть свои, но они должны по возможности исключать разночтения.
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Написание программы&quot; [33708-34233] --&gt;
&lt;h2&gt;&lt;a name=&quot;кодирование&quot; id=&quot;кодирование&quot;&gt;Кодирование&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#соблюдать_модульность&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Соблюдать модульность&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#избегать_условностей&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Избегать условностей&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#не_делать_длинных_и_сложных_выражений&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Не делать длинных и сложных выражений&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#операторные_скобки&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Операторные скобки&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#операторы_break_и_continue_во_вложенных_циклах&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Операторы break и continue во вложенных циклах&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#точность_вещественных_чисел&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Точность вещественных чисел&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#целочисленное_деление&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Целочисленное деление&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#правила_для_констант&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Правила для констант&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#заключать_константы_и_операнды_макросов_в_круглые_скобки&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Заключать константы и операнды макросов в круглые скобки&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#заключать_тела_макросов_в_фигурные_скобки&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Заключать тела макросов в фигурные скобки&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#правила_для_функций&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Правила для функций&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#использовать_сторожевой_таймер&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Использовать сторожевой таймер&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#атомарный_доступ&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Атомарный доступ&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Кодирование&quot; [34234-35131] --&gt;
&lt;h3&gt;&lt;a name=&quot;соблюдать_модульность&quot; id=&quot;соблюдать_модульность&quot;&gt;Соблюдать модульность&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;br/&gt;
 
&lt;p&gt;

Мы уже говорили о разбиении программы на модули, еще раз приведу &lt;span class=&quot;important&quot;&gt;преимущества модульности&lt;/span&gt;:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Мобильность&lt;/strong&gt; (легкий перенос модуля в другую программу)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Наглядность&lt;/strong&gt; (легкий поиск определений конкретных функций)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Заменяемость&lt;/strong&gt; (замена одного модуля другим при изменении условий работы, например, при смене внешнего оборудования)&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Раз мы решили разбить программу на модули, то нужно следовать этому решению до конца и не совершать каких-либо действий, нарушающих модульность. Т.е. наши модули должны обладать следующими &lt;span class=&quot;important&quot;&gt;характеристиками&lt;/span&gt;:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Самобытность&lt;/strong&gt;&lt;br/&gt;
 Функции и переменные, относящиеся к одному модулю, определять именно внутри этого модуля. Понятно, что иначе перенос модуля в другую программу обернется тем, что в каждой новой программе придется переопределять недовнесенные переменные.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Самодостаточность&lt;/strong&gt;&lt;br/&gt;
 Не использовать в модуле внешние переменные из верхних модулей. Опять же, причина в том, что при переносе модуля в другую программу придется в новой программе не только доопределять какие-то переменные, но и восстанавливать механизмы их работы с тем, чтобы модуль вел себя правильно.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Гибкость в настройке&lt;/strong&gt;&lt;br/&gt;
 Например, если это модуль для работы по шине i2c, то при переносе в другой проект должна быть возможность выбирать (или задавать константами) адрес устройства, разрядность адреса данных, выводы, к которым подключено устройство i2c.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Соблюдать модульность&quot; [35132-37703] --&gt;
&lt;h3&gt;&lt;a name=&quot;избегать_условностей&quot; id=&quot;избегать_условностей&quot;&gt;Избегать условностей&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;br/&gt;
 
&lt;p&gt;

В программе следует избегать любых конструкций, которые при прочтении могут быть истолкованы двояко, или которые смогут при определенных условиях повести себя неправильно. Самые частые допуски, которые позволяют себе программисты, - это несоблюдение границ использованных типов, путаница со знаковыми и беззнаковыми переменными, пренебрежение приведением типов. Эти допуски рассмотрим в первую очередь. 
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;типы_данных&quot; id=&quot;типы_данных&quot;&gt;Типы данных&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Нужно определять переменные тем типом, который соответствует их назначению. Например, не следует переменную, которая будет использована как числовая, определять типом char. Число может быть либо signed char либо unsigned char. Сам же char определяет символьную переменную. Понятно, что для контроллера все эти переменные – одно и то же, но для компилятора, а также для человека, читающего текст программы, - это разные вещи.
&lt;/p&gt;

&lt;p&gt;
Например, типичная ошибка:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:red; '&gt;Неправильно:&lt;/span&gt;&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; Counter;
Counter &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Counter &lt;span class=&quot;sy1&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; ...;&lt;/pre&gt;
&lt;p&gt;
Это выражение будет правильно обрабатываться в MPLAB C18, в то время как в HT-PICC18 оно всегда будет возвращать true. Все из-за того, что в стандарте языка не оговаривается знаковость типа char, и каждый разработчик компиляторов вправе толковать его по-своему. В приведенном примере переменная должна быть определена так:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:#00CC40; '&gt;Правильно:&lt;/span&gt;&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;signed&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;  Counter;
Counter &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Counter &lt;span class=&quot;sy1&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; ...;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;приведение_типов&quot; id=&quot;приведение_типов&quot;&gt;Приведение типов&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Не стоит надеяться, что компилятор всегда сам сделает приведение типов в случае, когда в выражении участвуют разнотипные переменные и константы. Всегда следует выполнять приведение типов вручную, исключая разночтения выражения программистом и компилятором.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:red; '&gt;Неправильно:&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;   i;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;p;
p &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;i;&lt;/pre&gt;
&lt;p&gt;
&lt;strong&gt;&lt;span style='color:#00CC40; '&gt;Правильно:&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;p &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;i;&lt;/pre&gt;
&lt;p&gt;
В конкретном примере мы, скорее всего, получим правильный результат и без приведения типов (хотя, возможны нюансы с однобайтовыми near-указателями для PIC18). Но бывает так, что программист, рассчитывая на автоматическое приведение типов, проморгает его отсутствие в более сложном выражении, например: выражение содержит подвыражения в скобках, в которых типы будут приведены только после выполнения подвыражения, т.е. тогда, когда, например, значимость будет уже потеряна (из-за переполнения). 
&lt;/p&gt;

&lt;p&gt;
То же самое относится к передаче параметров в функцию.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;побайтовое_обращение_к_многобайтовой_переменной&quot; id=&quot;побайтовое_обращение_к_многобайтовой_переменной&quot;&gt;Побайтовое обращение к многобайтовой переменной&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Пример &lt;strong&gt;&lt;span style='color:red; '&gt;неправильного обращения&lt;/span&gt;&lt;/strong&gt;:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; lo, hi;
&lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;  ui;
...
&lt;span class=&quot;me1&quot;&gt;lo&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;ui &lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
hi &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;ui &lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Дело в том, что стандартом языка не предусматривается порядок чередования байтов в многобайтовых объектах.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:#00CC40; '&gt;Правильное обращение&lt;/span&gt;&lt;/strong&gt;:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;lo &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; ui &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0xFF&lt;/span&gt;;
hi &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; ui &lt;span class=&quot;sy1&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;8&lt;/span&gt;;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;определение_функций&quot; id=&quot;определение_функций&quot;&gt;Определение функций&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
При определении функции следует указывать полностью входные и выходные типы. Если функция определена просто:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;myfunc &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
, то компилятор по умолчанию будет считать, что она возвращает int, и не принимает параметров. Однако не следует оставлять такой неопределенности. 
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    myfunc &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;       &lt;span class=&quot;co1&quot;&gt;// неправильно&lt;/span&gt;
    myfunc &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;   &lt;span class=&quot;co1&quot;&gt;// неправильно&lt;/span&gt;
&lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt; myfunc &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;       &lt;span class=&quot;co1&quot;&gt;// неправильно&lt;/span&gt;
&lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt; myfunc &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;   &lt;span class=&quot;co1&quot;&gt;// Правильно&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;пустые_операторы&quot; id=&quot;пустые_операторы&quot;&gt;Пустые операторы&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Не следует в теле цикла &lt;code&gt;while&lt;/code&gt;, а также в операторах &lt;code&gt;if…else&lt;/code&gt; использовать пустой оператор:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;  &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;TRMT&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;          &lt;span class=&quot;co1&quot;&gt;// Ожидаем освобождение буфера&lt;/span&gt;
  TXREG &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; Data;&lt;/pre&gt;
&lt;p&gt;
Нечаянно пропущенная ‘;’ обернется неправильным поведением программы. Лучше вместо пустого оператора ставить &lt;code&gt;continue&lt;/code&gt; либо &lt;code&gt;{}&lt;/code&gt;:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;  &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;TRMT&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;continue&lt;/span&gt;; &lt;span class=&quot;co1&quot;&gt;// Ожидаем освобождение буфера&lt;/span&gt;
  TXREG &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; Data;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;про_оператор_switch&quot; id=&quot;про_оператор_switch&quot;&gt;Про оператор switch&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
В операторе &lt;code&gt;switch&lt;/code&gt; нужно:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Определять ветку &lt;code&gt;default&lt;/code&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; В каждом &lt;code&gt;case&lt;/code&gt; ставить &lt;code&gt;break&lt;/code&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; неиспользуемые &lt;code&gt;break&lt;/code&gt; закрывать комментариями&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;...&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt;
         ...
         &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x01&lt;/span&gt;&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt;
         ...
         &lt;span class=&quot;co1&quot;&gt;// break; // Поставив закомментированный break, мы даем себе понять,&lt;/span&gt;
                   &lt;span class=&quot;co1&quot;&gt;// что после обработки условия 0x01 мы хотим перейти к коду&lt;/span&gt;
                   &lt;span class=&quot;co1&quot;&gt;// обработки условия 0x02, а не пропустили break случайно&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x02&lt;/span&gt;&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt;
         ...
         &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt;       &lt;span class=&quot;co1&quot;&gt;// Обязательная ветка, даже если есть уверенность, что&lt;/span&gt;
                   &lt;span class=&quot;co1&quot;&gt;// выражение в switch всегда принимает одно из описанных&lt;/span&gt;
                   &lt;span class=&quot;co1&quot;&gt;// значений&lt;/span&gt;
         &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;    &lt;span class=&quot;co1&quot;&gt;// Ветка default также должна заканчиваться break&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;неинициализированные_переменные&quot; id=&quot;неинициализированные_переменные&quot;&gt;Неинициализированные переменные&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Нельзя пользоваться неинициализированными переменными в расчете на то, что компилятор сам сгенерит код их инициализации (например, обнулит после сброса).
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;скобки_в_сложных_выражениях&quot; id=&quot;скобки_в_сложных_выражениях&quot;&gt;Скобки в сложных выражениях&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
В некоторых случаях в сложных выражениях есть смысл расставлять скобки даже тогда, когда есть уверенность в правильности приоритетов операций. Такие выражения легче анализировать, т.к. ошибка может быть не только в приоритетности операций, но и в самом выражении. Также это снижает вероятность внесения ошибки при модификации выражения.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;такая_ситуация_никогда_не_случится&quot; id=&quot;такая_ситуация_никогда_не_случится&quot;&gt;«Такая ситуация никогда не случится!»&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
На эту тему можно вообще долго говорить. Вот интересный фрагмент определения типа из одной из присланных мне программ:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;struct&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;   seconds &lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;6&lt;/span&gt;;
    &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;   minutes &lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;6&lt;/span&gt;;
    &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;   hours   &lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;;    &lt;span class=&quot;co1&quot;&gt;// Неправильно задана размерность&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; T_CLOCK;&lt;/pre&gt;
&lt;p&gt;
Обратите внимание на размерность полей данной структуры. Под минуты и под секунды выделено по 6 бит, чтобы охватить весь интервал 0..59. А под часы вместо 5 бит, выделено 4. Программист, написавший это, предположил, что т.к. программа будет работать только с 8 утра, то проще выделить 4 бита (покрывая интервал в оставшиеся 16 часов) с тем, чтобы вся структура влезла в два байта, а в самой программе к значению hours всегда прибавлять 8. Надо ли говорить, что сбой не заставил себя должго ждать?
&lt;/p&gt;

&lt;p&gt;
Так что не стоит забывать о первом законе Мерфи: «Если неприятность может случиться, - она случается».
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;мертвые_циклы&quot; id=&quot;мертвые_циклы&quot;&gt;Мертвые циклы&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Часто в программах встречаются участки кода, которые потенциально могут привести к зависанию. Самый распространенный пример – ожидание готовности или подтверждения при работе с внешней периферией:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:red; '&gt;Неправильно:&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; lcd_wait_ready &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;PIN_LCD_READY&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;continue&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Если произошла непредвиденная ситуация (обрыв линии, короткое замыкание, конденсат и пр.), то из этого цикла мы никогда не выйдем. (Разве что только WDT сработает). Поэтому нужно всегда предусматривать аварийный выход из таких циклов. Можно это делать с помощью таймера. 
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:#00CC40; '&gt;Правильно:&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;  lcd_wait_ready &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    TMR0   &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt;;              &lt;span class=&quot;co1&quot;&gt;// Готовим таймер для фиксации таймаута&lt;/span&gt;
    TMR0IF &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt;    &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;PIN_LCD_READY&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;TMR0IF&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;   &lt;span class=&quot;co1&quot;&gt;// Выходим с кодом ошибки&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;                   &lt;span class=&quot;co1&quot;&gt;// Выходим OK&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Правда, целый таймер выделять для этого иногда накладно, и есть смысл работать с глобальной переменной, которая будет уменьшаться в прерывании по таймеру:
 
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;co1&quot;&gt;// Фрагмент обработчика прерывания&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;TMR0IF &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; TMR0IE&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        TMR0IF &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
        TMR0 &lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; TMR0_CONST;     &lt;span class=&quot;co1&quot;&gt;// Обновляем таймер&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;--&lt;/span&gt;g_WaitTimer&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;     &lt;span class=&quot;co1&quot;&gt;// Проверяем переполнение&lt;/span&gt;
            g_Timeout &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
        ...
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
...
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;  lcd_wait_ready &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    g_WaitTimer &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;;           &lt;span class=&quot;co1&quot;&gt;// Готовим таймер для фиксации таймаута&lt;/span&gt;
    g_Timeout &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;PIN_LCD_READY&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;g_Timeout&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;&lt;span class=&quot;co1&quot;&gt;// Выходим с кодом ошибки&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;                   &lt;span class=&quot;co1&quot;&gt;// Выходим OK&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Кстати, заметьте, что в обработчике прерывания проверяется не только флаг конкретного прерывания (TMR0IF), но и бит разрешения (TMR0IE). Дело в том, что в PIC-контроллерах младшего и среднего семейства несколько прерываний могут обрабатываются одним обработчиком. И если у нас прерывание по TMR0 отключено (TMR0IE = 0), а в обработчик мы попали от другого источника (например RCIF), то без проверки битов xxxIE мы обработаем все отключенные прерывания, у которых на момент входа в обработчик оказался установлен флаг xxxIF.
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Избегать условностей&quot; [37704-50988] --&gt;
&lt;h3&gt;&lt;a name=&quot;не_делать_длинных_и_сложных_выражений&quot; id=&quot;не_делать_длинных_и_сложных_выражений&quot;&gt;Не делать длинных и сложных выражений&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;br/&gt;
 
&lt;p&gt;

Такие выражения не только трудно анализировать, но их также трудно тестировать и отлаживать.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:red; '&gt;Неправильно&lt;/span&gt;&lt;/strong&gt;:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;t &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kw3&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;p&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;r&lt;span class=&quot;sy2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;a&lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt;b&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;b&lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt;v&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;kw3&quot;&gt;sin&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw3&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;b&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;b&lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;a&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;c&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;SHIFT_CONST&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Представьте, что программа делает вычисления неправильно и есть подозрения, что проблема в этом выражении. А это выражение даже в симуляторе не прогнать. Такие выражения желательно разбивать на несколько подвыражений:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:#00CC40; '&gt;Правильно:&lt;/span&gt;&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;A &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; p&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;r;
B &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; a &lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt; b&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;b &lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt; v;
C &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; b&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;b – &lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;a&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;c;
D &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; SHIFT_CONST&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;B &lt;span class=&quot;sy1&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; ...;      &lt;span class=&quot;co1&quot;&gt;// Ошибка: «деление на 0»&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;C &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;  ...;      &lt;span class=&quot;co1&quot;&gt;// Ошибка: «корень из отрицательного числа»&lt;/span&gt;
&amp;nbsp;
E &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; A&lt;span class=&quot;sy2&quot;&gt;/&lt;/span&gt;B;
&lt;span class=&quot;kw1&quot;&gt;If&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;E &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;   ...;     &lt;span class=&quot;co1&quot;&gt;// Ошибка: «корень из отрицательного числа»&lt;/span&gt;
&amp;nbsp;
t &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kw3&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;E&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;kw3&quot;&gt;sin&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw3&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;C&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;/&lt;/span&gt;D&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Теперь наше выражение не только легко читается, но и легко тестируется и отлаживается, а кроме того, – еще и имеет механизм защиты от неправильных входных данных (этот механизм можно было бы оставить и с длинным выражением, но тогда некоторые части выражения пришлось бы пересчитывать дважды).
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Не делать длинных и сложных выражений&quot; [50989-52776] --&gt;
&lt;h3&gt;&lt;a name=&quot;операторные_скобки&quot; id=&quot;операторные_скобки&quot;&gt;Операторные скобки&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;br/&gt;
 
&lt;p&gt;

При написании фрагментов программ, содержащих вложенные циклы или вложенные условные операторы, желательно расставлять операторные скобки даже для однострочных блоков.
&lt;/p&gt;

&lt;p&gt;
Рассмотрим пример одной часто встречающейся ошибки:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:red; '&gt;Неправильно:&lt;/span&gt;&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;A &lt;span class=&quot;sy1&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;B &lt;span class=&quot;sy1&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; C &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt; C &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Замысел программиста был таков: если &lt;span class=&quot;important&quot;&gt;A&lt;/span&gt; равняется &lt;span class=&quot;important&quot;&gt;1&lt;/span&gt;, то при &lt;span class=&quot;important&quot;&gt;B&lt;/span&gt;, равном &lt;span class=&quot;important&quot;&gt;2&lt;/span&gt;, присвоить &lt;span class=&quot;important&quot;&gt;C&lt;/span&gt; значение &lt;span class=&quot;important&quot;&gt;3&lt;/span&gt;, иначе присвоить &lt;span class=&quot;important&quot;&gt;C&lt;/span&gt; значение &lt;span class=&quot;important&quot;&gt;4&lt;/span&gt;. Т.е. программист считал, что в &lt;span class=&quot;important&quot;&gt;C&lt;/span&gt; будет занесено значение &lt;span class=&quot;important&quot;&gt;4&lt;/span&gt;, если &lt;span class=&quot;important&quot;&gt;A&lt;/span&gt; не равно &lt;span class=&quot;important&quot;&gt;1&lt;/span&gt;. Однако на самом деле компилятор видит это по-другому: &lt;code&gt;else&lt;/code&gt; применяется к ближайшему &lt;code&gt;if&lt;/code&gt;, а не к тому, который выровнен с ним в тексте. В нашем случае &lt;code&gt;else&lt;/code&gt; относится к условию &lt;code&gt;if (b == 2)&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:#00CC40; '&gt;Правильно&lt;/span&gt;&lt;/strong&gt; это условие нужно было записать так:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;A &lt;span class=&quot;sy1&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;B &lt;span class=&quot;sy1&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; C &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt; C &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;;&lt;/pre&gt;&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Операторные скобки&quot; [52777-54283] --&gt;
&lt;h3&gt;&lt;a name=&quot;операторы_break_и_continue_во_вложенных_циклах&quot; id=&quot;операторы_break_и_continue_во_вложенных_циклах&quot;&gt;Операторы break и continue во вложенных циклах&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;br/&gt;
 
&lt;p&gt;

Часто встречающейся ошибкой является использование &lt;code&gt;break&lt;/code&gt; или &lt;code&gt;continue&lt;/code&gt; во вложенных циклах или операторе &lt;code&gt;switch&lt;/code&gt; в расчете на то, что эти операторы сработают в рамках и внешнего цикла. Вот пример из реальной программы (он немного порезан для наглядности, и ошибка сразу бросается в глаза), который подсчитывал количество положительных и отрицательных единиц в массиве, причем нулевой элемент был признаком конца массива.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:red; '&gt;Неправильно:&lt;/span&gt;&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;Positive &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; Negative &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; i &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; MAX_ARRAY_SIZE; i&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;array&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;case&lt;/span&gt;  &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;       &lt;span class=&quot;co1&quot;&gt;// Выйти из цикла (ошибка, т.к. выйдем&lt;/span&gt;
                           &lt;span class=&quot;co1&quot;&gt;// только из switch)&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;case&lt;/span&gt;  &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt;
              Positive++;
              &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt;
              Negative++;
              &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Программист решил использовать для проверки конструкцию &lt;code&gt;switch&lt;/code&gt;, в которой, среди прочего, при нахождении элемента со значением 0 счет должен был прерваться. Однако, очевидно, что &lt;code&gt;break&lt;/code&gt; в ветке case 0 выведет программу из &lt;code&gt;switch&lt;/code&gt;&amp;#039;а, а не из цикла &lt;code&gt;for&lt;/code&gt;. 
&lt;/p&gt;

&lt;p&gt;
Есть несколько способов решить эту проблему. Один из вариантов в данном случае – использовать дополнительный &lt;code&gt;if&lt;/code&gt;:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:#00CC40; '&gt;Правильно:&lt;/span&gt;&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;Positive &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; Negative &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; i &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; MAX_ARRAY_SIZE; i&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;array&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;       &lt;span class=&quot;co1&quot;&gt;// Выйти из цикла&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;array&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;case&lt;/span&gt;  &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt;
              Positive++;
              &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt;
              Negative++;
              &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Однако он не очень эффективный, т.к. приводит к повторному вычислению адреса элемента массива при косвенной адресации. Есть и другие варианты решения проблемы. Главное – помнить, что &lt;code&gt;break&lt;/code&gt;/&lt;code&gt;continue&lt;/code&gt; прерывает/продолжает только текущий цикл (или &lt;code&gt;switch&lt;/code&gt;).
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Операторы break и continue во вложенных циклах&quot; [54284-57096] --&gt;
&lt;h3&gt;&lt;a name=&quot;точность_вещественных_чисел&quot; id=&quot;точность_вещественных_чисел&quot;&gt;Точность вещественных чисел&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;br/&gt;
 
&lt;p&gt;

Когда появляется необходимость работы с вещественными числами с плавающей запятой, нужно внимательнее изучить теорию об их структуре (мантисса, порядок, знаки). У некоторых программистов, которые с ней не знакомы, при виде диапазона +/- 1.7e38 появляется иллюзия всемогущества и универсальности этого типа данных. При этом из вида упускаются такие важные детали, как потеря значимости, нормирование, переполнение, относительная погрешность. 
&lt;/p&gt;

&lt;p&gt;
В одной программе я видел такой фрагмент:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;long&lt;/span&gt; l;
&lt;span class=&quot;kw4&quot;&gt;float&lt;/span&gt; f;
f &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu16&quot;&gt;0.0&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;l &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; l &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;100000L&lt;/span&gt;; l&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    ...;
    f &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; f &lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nu16&quot;&gt;1.0&lt;/span&gt;;
    ...;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;em&gt;(Примечание: в программе был использован вещественный тип размерностью 24-бита.)&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
На месте троеточий делались кое-какие вычисления, одним из параметров в которых была переменная f. В этом цикле первые две трети результатов оказывались правильными, а дальше появлялись отклонения, причем, чем дальше, тем сильнее.
&lt;/p&gt;

&lt;p&gt;
А происходило следующее: когда переменная f досчитывала до значения 65536 (т.е. выходила за пределы 16-разрядной мантиссы), ее экспонента (порядок) увеличивалась на единицу. При этом вес младшего разряда мантиссы оказывался равным 2. При выполнении сложения двух вещественных чисел f и 1.0 сперва производится приведение их к общей экспоненте, в результате которого 1.0 превращается в 0, т.к. при сложении в обоих числах порядок выравнивается в большую сторону, т.е. вес младшего разряда мантиссы в обоих операндах будет равен 2, при этом происходит потеря значимости, и результат сложения 65536.0 + 1.0 будет равен 65536.0.
&lt;/p&gt;

&lt;p&gt;
Также стоит отметить, что в общем случае для вещественных чисел нельзя проверять деление умножением. 
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Точность вещественных чисел&quot; [57097-60042] --&gt;
&lt;h3&gt;&lt;a name=&quot;целочисленное_деление&quot; id=&quot;целочисленное_деление&quot;&gt;Целочисленное деление&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;br/&gt;
 
&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;округление&quot; id=&quot;округление&quot;&gt;Округление&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Часто встречается такой допуск при делении целых чисел, будто бы компилятор самостоятельно должен производить округление. Так вот: он этого не делает, это должен делать сам программист. Типичная ошибка, например, при расчете скорости USART&amp;#039;а:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:red; '&gt;Неправильно:&lt;/span&gt;&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define FOSC       200000000L&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define BAUDRATE      115200L&lt;/span&gt;
...
&lt;span class=&quot;me1&quot;&gt;SPBRG&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; FOSC &lt;span class=&quot;sy2&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;BAUDRATE&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; – &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
При расчете на бумаге все выглядит красиво: 

&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  20000000/(115200*16) – 1 = 9.85 &lt;/pre&gt;

&lt;p&gt;

и округляется в большую сторону до 10. Ошибка скорости при этом составляет:

&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  E = |10 - 9.85| / 9.85 * 100% = 1.5%&lt;/pre&gt;

&lt;p&gt;

, что в допустимых пределах.
&lt;/p&gt;

&lt;p&gt;
Однако, когда мы поручаем компилятору вычислить формулу в том виде, в котором мы ее привели, он округление произведет не по правилам математики (&amp;lt;0.5 – в меньшую сторону, &amp;gt;=0.5 – в большую), а просто откинет дробную часть. В результате ошибка будет уже:

&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  E = |9 - 9.85| / 9.85 * 100% = 8.6%&lt;/pre&gt;

&lt;p&gt;

, т.е. в 4 раза превышает допустимую.
&lt;/p&gt;

&lt;p&gt;
Та же проблема всплывает при любых расчетах с целочисленными переменными и константами, где присутствует операция деления. 
&lt;/p&gt;

&lt;p&gt;
Для того чтобы округление производилось правильно, нужно к числителю прибавлять половину знаменателя:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:#00CC40; '&gt;Правильно:&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    SPBRG &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;FOSC &lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt; BAUDRATE&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;BAUDRATE&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; – &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
В общем случае формула “A = B / C” при использовании целых чисел в программе должна быть записана так:

&lt;/p&gt;
&lt;table class=&quot;inline&quot;&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;th class=&quot;col0 centeralign&quot;&gt;  &lt;br/&gt;
    &lt;span class=&quot;important&quot;&gt;A = (B + C / 2) / C;&lt;/span&gt;   &lt;br/&gt;
 &lt;br/&gt;
  &lt;/th&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;

Операцию деления на 2 (в выражении C/2) целесообразно заменять сдвигом вправо на один разряд. Обратите внимание, что деление на 2 – это то же самое целочисленное деление, но выражение записывается C/2 , а не (C+1)/2, т.е. к числителю не прибавляется половина знаменателя. Почему? Не вдаваясь в подробности насчет различия погрешностей при делении на разные значения (в нашем примере деление на 2 и деление на C), приведу тривиальный пример: «разделить 1 на 1». Результат деления должен быть равен 1:

&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  (1 + 1 / 2) / 1 = (1 + 0) / 1 = 1&lt;/pre&gt;

&lt;p&gt;

Если мы при делении на 2 в дроби 1/2 прибавим к числителю половину знаменателя, то получится:

&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  (1 + (1 + 2/2) / 2) / 1 = (1 + 2 / 2) / 1 = (1+1) / 1 = 2&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;последовательность_делений_и_умножений&quot; id=&quot;последовательность_делений_и_умножений&quot;&gt;Последовательность делений и умножений&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Дополню еще одной рекомендацией: если в выражении присутствуют операции как умножения так и деления, то формулу лучше переписать так, чтобы сперва выполнялись операции умножения, а затем деления:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:red; '&gt;Неправильно:&lt;/span&gt;&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;A = B / C * D;&lt;/pre&gt;

&lt;p&gt;

&lt;strong&gt;&lt;span style='color:#00CC40; '&gt;Правильно:&lt;/span&gt;&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;A = B * D / C;&lt;/pre&gt;

&lt;p&gt;

Причины очевидны: так как приоритет операций умножения и деления одинаков, то выражение будет вычисляться слева направо, причем округление будет выполняться после каждой операции. Таким образом, в первой формуле на D будет умножено не только отношение B/C, но еще и ошибка округления. 
&lt;/p&gt;

&lt;p&gt;
Рассмотрим пример: «2 / 3 * 3». Результат выражения должен быть равен 2. Сначала перепишем это выражение по правилам округления, как было описано выше (иначе 2/3 дадут результат 0, и результат всего выражения тоже будет нулевой):
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;
  (2 / 3) * 3 -&amp;gt; ((2 + 3/2) / 3) * 3 = ((2 + 1) / 3) * 3 = (3/3) * 3 = 1 * 3 = 3&lt;/pre&gt;

&lt;p&gt;

Как видим, мы получили неправильный ответ. Почему? Потому что на 3 была умножена еще и ошибка округления отношения 2/3. Ошибка была равна 1/3, и, помножив ее на 3, мы получили лишнюю 1 (проблема произойдет также и при округлении вниз). Правильно было сначала произвести умножение, а потом деление, т.е. «2*3/3»:

&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  (2 * 3) / 3 -&amp;gt; (2 * 3 + 3/2) / 3 = (6 + 1) / 3 = 7 / 3 = 2&lt;/pre&gt;

&lt;p&gt;

Есть один тонкий момент – это переполнение, которое при перемножении нескольких чисел может возникнуть еще до того, как придет очередь до операторов деления. Но это можно предусмотреть на этапе разработки, взяв для вычислений числа большей разрядности.
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Целочисленное деление&quot; [60043-66267] --&gt;
&lt;h3&gt;&lt;a name=&quot;правила_для_констант&quot; id=&quot;правила_для_констант&quot;&gt;Правила для констант&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;br/&gt;
 
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#не_использовать_числовые_константы&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Не использовать числовые константы&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#указывать_тип_константы&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Указывать тип константы&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#задавать_константам_осмысленные_значения&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Задавать константам осмысленные значения&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#два_слова_о_проверке_правильности_задания_констант&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Два слова о проверке правильности задания констант&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#соблюдать_систему_счисления&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Соблюдать систему счисления&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;не_использовать_числовые_константы&quot; id=&quot;не_использовать_числовые_константы&quot;&gt;Не использовать числовые константы&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Речь идет о неиспользовании числовых констант в оперативной части кода. Например, в программе предполагается использовать массив размерностью 25 элементов. Не следует по всему тексту программы явно указывать константу 25. 
&lt;/p&gt;

&lt;p&gt;
Пример &lt;strong&gt;&lt;span style='color:red; '&gt;неправильного подхода:&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; String&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;
...
&lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; i &lt;span class=&quot;sy1&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;24&lt;/span&gt;; i&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; String&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; ‘ ‘;&lt;/pre&gt;
&lt;p&gt;
&lt;strong&gt;&lt;span style='color:#00CC40; '&gt;Правильно&lt;/span&gt;&lt;/strong&gt; в одном месте программы определить константу, а потом в тексте оперировать только ее именем:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define STRING_SIZE      25&lt;/span&gt;
...
&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; String&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;STRING_SIZE&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;
...
&lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; i &lt;span class=&quot;sy1&quot;&gt;&amp;lt;=&lt;/span&gt; STRING_SIZE &lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;; i&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; String&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; ‘ ‘;&lt;/pre&gt;
&lt;p&gt;
Дело в том, что если по каким-то причинам придется изменить размерность массива, то придется и по всему тексту программы выискивать все относящиеся к размерности фрагменты и исправлять их. Пропустив всего один такой фрагмент, можно получить редко проявляющийся но довольно разрушительный баг. (Обратите внимание на то, что применен оператор &amp;quot;&amp;lt;= 24&amp;quot;, а не &amp;quot;&amp;lt; 25&amp;quot;; я специально сделал для примера такой вариант, чтобы было понятно, что при изменении размерности массива простой поиск с заменой может не дать полноценного результата.) 
&lt;/p&gt;

&lt;p&gt;
Поэтому всем константам, не являющимся неоспоримыми (например, в минуте 60 секунд, в килобайте 1024 байта, при переводе в десятичную систему всегда число делим на 10 и т.д.), следует давать имена и в программе работать только с именованными константами.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;указывать_тип_константы&quot; id=&quot;указывать_тип_константы&quot;&gt;Указывать тип константы&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:red; '&gt;Неправильно:&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define FOSC               4000000&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define MAX_INTERATIONS      10000&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define MIDDLE_TEMPERATURE      25&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Если с определением FOSC сомнений не возникает (компилятор однозначно поймет, что это 32-битная константа), то с &lt;code&gt;MAX_ITERATIONS&lt;/code&gt; будут проблемы. Если где-нибудь в коде встретится выражение:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;MAX_ITERATIONS &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
, то результатом его будет не 100000, а… -31072. Почему? Потому что по умолчанию константа будет рассматриваться как знаковое 16-разрядное целое. Результат 100000 выходит за границы 16 разрядов, поэтому он будет обрезан до 0x86A0, что соответствует -31072.
&lt;/p&gt;

&lt;p&gt;
То же самое касается констант, которые должны быть вещественными. Если в программе встретится выражение:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;MIDDLE_TEMPERATURE &lt;span class=&quot;sy2&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;26&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
, то результатом будет 0, а не предполагаемые 25/26 = 0,96.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:#00CC40; '&gt;Правильно:&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define FOSC               4000000L&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define MAX_INTERATIONS      10000L&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define MIDDLE_TEMPERATURE      25.0&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;задавать_константам_осмысленные_значения&quot; id=&quot;задавать_константам_осмысленные_значения&quot;&gt;Задавать константам осмысленные значения&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Вот пример из реальной программы:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define BAUDRATE     24     // 9600&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
В функции инициализации периферии была такая строчка:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;SPBRG &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; BAUDRATE;&lt;/pre&gt;
&lt;p&gt;
Программа должна была работать с тактовой частотой 4 МГц. А для такой тактовой частоты константа BAUDRATE должна иметь значение 25, а не 24. Таким образом, ошибка скорости составила 4%, что, в общем-то, почти на грани допустимого. В результате иногда принимались не те данные, которые ожидались.  В разговоре с программистом выяснилось, что ранее программа работала по USART на скорости 19200 (константа BAUDRATE=12), но потом из-за смены внешнего оборудования пришлось изменить скорость на 9600, что программист и сделал, умножив константу на 2.
&lt;/p&gt;

&lt;p&gt;
Но беда еще и в том, что даже с комментарием «9600» такое определение константы может заставлять задумываться. Ведь если при отладке выясняется, что что-то не так с приемом/передачей по USART, то эту константу придется проверять по формуле. Не говоря уже о том, что при выборе другой скорости или при смене тактовой частоты константу придется пересчитывать. И не надо забывать, что в программу может заглядывать не только автор, а для чужого человека заглядывание в подобный код равносильно написанию собственного.
&lt;/p&gt;

&lt;p&gt;
Понятное дело, что в нашем примере программист просто ошибся с формулой (т.к. должен был сперва прибавить 1, потом умножить на 2, а затем отнять 1), но ошибки можно было бы избежать, если бы константа задавалась осмысленной:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define FOSC     40000000L&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define BAUDRATE     9600L&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
А уже при присваивании регистру SPBRG переводить эту константу в нужный вид с учетом тактовой частоты, также заданной в виде константы.
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;SPBRG &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;FOSC &lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt; BAUDRATE&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;BAUDRATE&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; – &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Итак, константы лучше задавать в том виде, в котором мы привыкли их видеть, т.е.:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; время задавать в секундах, а не в тиках 65536 мкс;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; давление – в паскалях (или в барах), а не в единицах АЦП;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Напряжение – в вольтах;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; температуру – в градусах;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; частоту – в герцах.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Это, во-первых, сделает программу более читабельной, а во-вторых, поможет избежать ошибок, которые будут являться следствием пересчетов из одной системы в другую. Правильнее один раз настроить формулу преобразования, чем каждый раз по ней же пересчитывать константы.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;два_слова_о_проверке_правильности_задания_констант&quot; id=&quot;два_слова_о_проверке_правильности_задания_констант&quot;&gt;Два слова о проверке правильности задания констант&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Задавая константу в понятном для нас виде, хотелось бы одновременно быть уверенным в том, что ее значения не выходят за рамки возможностей контроллера. Например, BAUDRATE из нашего примера должна обеспечивать точность скорости +/- 2%. Или давление, которое мы задаем в барах, должно быть таким, чтобы при переводе его в единицы АЦП получилось значение в пределах 1023. Поэтому в программе нужно блокировать неправильно заданные константы условными директивами и сообщениями об ошибке. Например:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co1&quot;&gt;// Задание параметров&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co2&quot;&gt;#define FOSC         4000000L&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define BAUDRATE        9600L&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;// Вычисление ошибки (в процентах)&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co2&quot;&gt;#define SPBRG_CONST    ((FOSC + BAUDRATE*8) / (BAUDRATE*16) - 1)&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define REAL_BAUDRATE  ((FOSC + (SPBRG_CONST+1)*8)/((SPBRG_CONST + 1)*16))&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define BAUDRATE_ERROR ((100L * (BAUDRATE - REAL_BAUDRATE) + BAUDRATE/2) / (BAUDRATE))&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;// Проверка ошибки на диапазон -2%..+2%&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co2&quot;&gt;#if BAUDRATE_ERROR &amp;lt; -2 || BAUDRATE_ERROR &amp;gt; 2&lt;/span&gt;
    &lt;span class=&quot;co2&quot;&gt;#error &amp;quot;Неправильно задана константа BAUDRATE&amp;quot;&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#endif&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Выглядит сложно, но сложность компенсируется тем, что эта формула должна быть проверена и отлажена один раз.
&lt;/p&gt;

&lt;p&gt;
&lt;p&gt;&lt;div class=&quot;notewarning&quot;&gt;
&lt;strong&gt;Примечание.&lt;/strong&gt; В компиляторах HTPICC и HTPICC18 проверка константы BAUDRATE_ERROR директивой #if работать не будет (из-за &lt;a href=&quot;http://www.microchip.su/showthread.php?t=7250&quot; class=&quot;urlextern&quot; title=&quot;http://www.microchip.su/showthread.php?t=7250&quot;  rel=&quot;nofollow&quot;&gt;ошибки компилятора&lt;/a&gt;). Проверку нужно делать либо в runtime&amp;#039;е, либо использовать другую формулу.

&lt;/div&gt;&lt;/p&gt;
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;соблюдать_систему_счисления&quot; id=&quot;соблюдать_систему_счисления&quot;&gt;Соблюдать систему счисления&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
При задании константы нужно соблюдать ту систему счисления, которая подходит константе по сути. Бывает, делают так:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:red; '&gt;Неправильно:&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;TRISA &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;21&lt;/span&gt;;    &lt;span class=&quot;co1&quot;&gt;// RA0,RA2,RA4 – вход, RA1, RA3 - выход&lt;/span&gt;
TRISB &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;65&lt;/span&gt;;    &lt;span class=&quot;co1&quot;&gt;// 65 = 0x41 = 0b01000001&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
В данном случае нужно задавать константу именно в бинарном виде (хотя, надо помнить, что бинарная запись числа не предусмотрена стандартом Си; тем не менее, почти все компиляторы для PIC&amp;#039;ов такую запись понимают). Каково будет программисту поменять один бит в такой записи?
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:#00CC40; '&gt;Правильно:&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define TRISA_CONST     0b00010101      // RA0,RA2,RA4 – входы&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define TRISB_CONST     0b01000001      // RB0, RB6 - входы&lt;/span&gt;
...
&lt;span class=&quot;me1&quot;&gt;TRISA&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; TRISA_CONST;
TRISB &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; TRISB_CONST;&lt;/pre&gt;
&lt;p&gt;
В этом случае четко видно, где единицы – там входы, нули выходы.
&lt;/p&gt;

&lt;p&gt;
Также часто встречается довольно нелепая запись:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:red; '&gt;Неправильно:&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;TMR1H &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0xF0&lt;/span&gt;;
TMR1L &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0xD8&lt;/span&gt;;     &lt;span class=&quot;co1&quot;&gt;// Отсчет 10000 тактов&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Хорошо еще, если ее снабдят комментариями, хотя, как мы убедились на примере с BAURDATE, комментарий не гарантирует правильности константы. Опять же большие трудности возникают при пересчете констант. В данном случае нужно пользоваться именно макросом (тут есть тонкость для контроллеров PIC18: TMR1H – это буферный регистр, и нам важна последовательность присваивания: сначала старший, затем – младший; стандартом языка последовательность не предусмотрена).
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:#00CC40; '&gt;Правильно:&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define TMR1_WRITE(timer)    { TMR1H = timer &amp;gt;&amp;gt; 8; TMR1L = timer &amp;amp; 0xFF; }&lt;/span&gt;
...
&lt;span class=&quot;me1&quot;&gt;TMR1_WRITE&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Правила для констант&quot; [66268-79468] --&gt;
&lt;h3&gt;&lt;a name=&quot;заключать_константы_и_операнды_макросов_в_круглые_скобки&quot; id=&quot;заключать_константы_и_операнды_макросов_в_круглые_скобки&quot;&gt;Заключать константы и операнды макросов в круглые скобки&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;br/&gt;
 
&lt;p&gt;

Типичная ошибка при определении числовых констант в виде выражения – не использование скобок. 
&lt;/p&gt;

&lt;p&gt;
Вот пример &lt;strong&gt;&lt;span style='color:red; '&gt;неправильного определения:&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define PIN_MASK_1    1 &amp;lt;&amp;lt; 2&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define PIN_MASK_2    1 &amp;lt;&amp;lt; 5&lt;/span&gt;
...
&lt;span class=&quot;me1&quot;&gt;PORTB&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; PIN_MASK_1 &lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt; PIN_MASK_2;&lt;/pre&gt;
&lt;p&gt;
Данное выражение развернется компилятором в такое:

&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  PORTB = 1 &amp;lt;&amp;lt; 2 + 1 &amp;lt;&amp;lt; 5;&lt;/pre&gt;

&lt;p&gt;

Вспомним, что приоритет операции «+» выше, чем приоритет сдвига, и получим выражение:

&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  PORTB = 1 &amp;lt;&amp;lt; (2 + 1) &amp;lt;&amp;lt; 5 = 1 &amp;lt;&amp;lt; 8&lt;/pre&gt;

&lt;p&gt;

Т.е. совсем не то, что мы ожидали. Ошибки можно было бы избежать, взяв выражения при определении констант в скобки, т.е. 
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:#00CC40; '&gt;правильное определение:&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define PIN_MASK_1    (1 &amp;lt;&amp;lt; 2)&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define PIN_MASK_2    (1 &amp;lt;&amp;lt; 5)&lt;/span&gt;
...
&lt;span class=&quot;me1&quot;&gt;PORTB&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; PIN_MASK_1 &lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt; PIN_MASK_2;&lt;/pre&gt;
&lt;p&gt;
Транслируется в:

&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;PORTB = (1 &amp;lt;&amp;lt; 2) + (1 &amp;lt;&amp;lt; 5);&lt;/pre&gt;

&lt;p&gt;
&lt;br/&gt;
 
&lt;br/&gt;
 
Из этой же области &lt;strong&gt;часто совершаемая ошибка – не заключение в скобки операндов макросов&lt;/strong&gt;. Например, имеем макрос:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:red; '&gt;Неправильно:&lt;/span&gt;&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define MUL_BY_3(a)    a * 3&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Выглядит просто и красиво, однако, попробуем вызвать макрос так:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; MUL_BY_3&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
И вместо предполагаемого результата 15 получим результат 7. Дело в том, что макрос развернется в следующее выражение:

&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  i = 4 + 1 * 3&lt;/pre&gt;

&lt;p&gt;

Ошибки можно было бы избежать, взяв аргумент макроса в скобки:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:#00CC40; '&gt;Правильно:&lt;/span&gt;&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define MUL_BY_3(a)   (a) * 3&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Тогда выражение примет вид:
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  i = (4 + 1) * 3;&lt;/pre&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Заключать константы и операнды макросов в круглые скобки&quot; [79469-81778] --&gt;
&lt;h3&gt;&lt;a name=&quot;заключать_тела_макросов_в_фигурные_скобки&quot; id=&quot;заключать_тела_макросов_в_фигурные_скобки&quot;&gt;Заключать тела макросов в фигурные скобки&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;br/&gt;
 
&lt;p&gt;

Тематика схожа с предыдущей. Рассмотрим пример:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:red; '&gt;Неправильно:&lt;/span&gt;&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define I2C_CLOCK()    NOP(); SCL = 1; NOP(); SCL = 0;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Т.е. выдерживаем паузу в один такт, затем формируем импульс длительностью два такта. Но такое определение может сыграть с нами злую шутку, если макрос будет использоваться внутри условного оператора:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;...&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; I2C_CLOCK&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Компилятор развернет макрос следующим образом:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;...&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; NOP&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;; SCL &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;; NOP&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;; SCL &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Как видно, условием отрезается только один NOP(), а сам импульс все-таки формируется. Ошибки можно было бы избежать, взяв тело макроса в фигурные скобки:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:#00CC40; '&gt;Правильно:&lt;/span&gt;&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define I2C_CLOCK()    do { NOP(); SCL = 1; NOP(); SCL = 0; } while (0)&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Такие ошибки крайне трудно отслеживать, т.к. на вид все выглядит правильно, и ошибку будем искать перед условием, после условия, в выражении условия, но не в I2C_CLOCK(). 
&lt;/p&gt;

&lt;p&gt;
Обратим внимание, что использована конструкция &lt;code&gt;do {…} while (0)&lt;/code&gt;. Если бы мы ее не использовали, то 
постановка &lt;code&gt;else&lt;/code&gt; в нашем условии:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;...&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; I2C_CLOCK&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;; &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;return&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;

привела бы к сообщению компилятора об ошибке &amp;quot;inappropriate else&amp;quot;. Все дело в том, что мы перед else и после &amp;#039;}&amp;#039; ставим &amp;#039;;&amp;#039;, которая воспринимается компилятором как конец оператора if. Поэтому и использованы скобки в виде do {…} while.
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Заключать тела макросов в фигурные скобки&quot; [81779-84050] --&gt;
&lt;h3&gt;&lt;a name=&quot;правила_для_функций&quot; id=&quot;правила_для_функций&quot;&gt;Правила для функций&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;br/&gt;
 &lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#объявлять_прототипы_для_всех_функций&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Объявлять прототипы для всех функций&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#проверять_входные_аргументы_функций_на_правильность&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Проверять входные аргументы функций на правильность&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#возвращать_функцией_код_ошибки&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Возвращать функцией код ошибки&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#не_делать_очень_больших_функций&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Не делать очень больших функций&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;объявлять_прототипы_для_всех_функций&quot; id=&quot;объявлять_прототипы_для_всех_функций&quot;&gt;Объявлять прототипы для всех функций&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Для любой функции, объявленной в модуле, нужно объявлять прототип в начале файла с тем, чтобы не задумываться при вызовах, где функция определена: выше вызова или ниже. Кроме того, при исследовании исходного текста программы это сразу дает общую картину: какие функции есть в модуле, какие параметры они принимают и какие значения возвращают. Вдобавок это позволяет сгруппировать наиболее значимые функции в начале файла, а все вспомогательные определить где-нибудь внизу, чтобы не мешались. 
&lt;/p&gt;

&lt;p&gt;
В заголовочный же файл нужно выносить прототипы только тех функций, которые будут вызываться из других модулей.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;проверять_входные_аргументы_функций_на_правильность&quot; id=&quot;проверять_входные_аргументы_функций_на_правильность&quot;&gt;Проверять входные аргументы функций на правильность&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Перед выполнением каких-либо операций с данными нужно убеждаться в их правильности:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Инициализированные указатели;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Попадают в область допустимых значений (например, номер элемента в массиве должен быть меньше размерности массива, чтобы не произошла запись в стороннюю ячейку); строка, содержащая имя, не может состоять из цифр.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Соответствуют реальности (например, счетчик битов в байте не может быть больше 8; температура в комнате не может быть -128 градусов Ц);&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Часто область допустимых значений и соответствие реальности – это одно и то же. Однако, есть случаи, когда требуются дополнительные проверки. Например, если параметром является структура, то можно сначала определить, что значение каждого поля в отдельности попадает в область допустимых значений, а потом по совокупности проверять соответствие данных друг другу.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;возвращать_функцией_код_ошибки&quot; id=&quot;возвращать_функцией_код_ошибки&quot;&gt;Возвращать функцией код ошибки&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Желательно предусмотреть возможность любой функцией возвращать код ошибки. Например, при неправильных входных данных, при ошибке в вычислениях (деление на ноль, переполнение), при ошибке работы с внешними устройствами. Причем, учитывая, что сама ошибка может произойти на самом нижнем уровне вложенных функций, нужно предусмотреть возможность передачи значения ошибки наверх.
&lt;/p&gt;

&lt;p&gt;
Для возврата кода ошибки иногда удобно пользоваться неиспользуемой результатом области значений. Но есть функции, возвращающие результат, который может принять любое значение из множества значений используемого типа. Например, функция перемножения двух чисел, или функция чтения байта из EEPROM. Также может вызвать трудности ситуация, когда вложенные функции возвращают результаты разных типов, что может помешать сквозной передаче кода ошибки. В таких случаях есть смысл пользоваться глобальной переменной.  Однако, такой вариант затруднителен при использовании сторонних библиотек.
&lt;/p&gt;

&lt;p&gt;
Все коды ошибок должны быть предопределены константами. Нежелательно пользоваться для их определения конструкцией &lt;code&gt;enum&lt;/code&gt; с неинициализированными значениями, т.к. после первого релиза все будут снабжены спецификацией, где ошибки будут расписаны по номерам, а в следующей версии будет добавлен еще один код ошибки (втиснут куда-то в середину списка), из-за чего половина констант изменят свое состояние. Поэтому их нужно определять либо через &lt;code&gt;#define&lt;/code&gt;, либо через инициализированный список перечислений:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw2&quot;&gt;enum&lt;/span&gt; ENUM_ERROR_CODES
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;// Математические ошибки&lt;/span&gt;
    ERROR_DIV_BY_ZERO &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x100&lt;/span&gt;
    ERROR_OVERFLOW,
    ERROR_UNDERFLOW,
    ...
    &lt;span class=&quot;co1&quot;&gt;// Ошибки работы с EEPROM&lt;/span&gt;
    ERROR_EEPROM_READ &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x110&lt;/span&gt;
    ERROR_EEPROM_WRITE,
    ERROR_EEPROM_INIT,
    ...
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Следует отметить, что коды ошибок, генерируемые функциями, - это не те ошибки, которые можно показывать пользователю. Например «ДЕЛЕНИЕ НА НОЛЬ» на экране стиральной машины приведет хозяйку в панику, если внутри ее любимая блузка. Я уж не берусь предугадать действия суеверного человека, если ему вывести шестнадцатеричное представление числа 57005.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;не_делать_очень_больших_функций&quot; id=&quot;не_делать_очень_больших_функций&quot;&gt;Не делать очень больших функций&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Эта рекомендация схожа с рекомендацией «Не делать длинных выражений». Если функция получается длинной, то есть смысл разбить ее на несколько функций. Это даст нам следующие преимущества:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; упростит внутреннюю логику функции; &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; сделает ее более читабельной; &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; упростит тестирование и отладку.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Правила для функций&quot; [84051-91567] --&gt;
&lt;h3&gt;&lt;a name=&quot;использовать_сторожевой_таймер&quot; id=&quot;использовать_сторожевой_таймер&quot;&gt;Использовать сторожевой таймер&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;br/&gt;
 
&lt;p&gt;

Сторожевой таймер в контроллерах имеет три применения: 

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; пробуждение контроллера из режима SLEEP с заданным инетервалом;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; произведение аппаратного сброса при зависании программы.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Для PIC-контроллеров младшего семейства – выполнение программного сброса&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Рассмотрим для начала второе применение, т.е. использование его в качестве дополнительного механизма защиты от зависания. Понятно, что лучше создавать правильные независающие алгоритмы и описывать их правильными независающими программами, но, к сожалению, часто  даже в отлаженной и переотлаженной программе могут скрываться ошибки, которые при определенном стечении обстоятельств приведут к зависанию. Вдобавок можно сказать, что сложные программы с большим количеством состояний и параллельных процессов бывает сложно отладить и протестировать полностью. Полное тестирование таких программ может затянуться на время, превышающее экономически целесообразные сроки выпуска устройства. В таких случаях приходится идти на некий компромисс между полнотой теста и сроками выпуска, т.е. программа может выйти, грубо говоря, недоотлаженной. Это не значит, что она будет сбоить на каждом шагу и выдавать какие-то результаты, не соответствующие спецификации. Это значит, что при определенных  условиях (чаще при совокупности внештатных ситуаций) возможно неадекватное поведение программы. И здесь нас может выручить сторожевой таймер, который не даст программе зависнуть  наглухо. Конечно, он не является панацеей от неправильно составленного или неправильно запрограммированного алгоритма. Он всего лишь перезапустит программу, но не исправит допущенной ошибки. Тем не менее, с ним, по крайней мере, есть возможность восстановить работоспособность устройства.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;когда_нужно_обрабатывать_wdt&quot; id=&quot;когда_нужно_обрабатывать_wdt&quot;&gt;Когда нужно обрабатывать WDT&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Во многих программах вижу «интересный» прием программистов: в битах конфигурации включали WDT, а в самой программе сбрасывали его везде, где попало, лишь бы не произошло переполнение: и в главном цикле программы, и в прерывании, и в функции задержки. Сделают так – и радуются: «Ай, какую я надежную программу написал! С вочдогом!» Этот подход эквивалентен подходу без использования WDT с той разницей, что у программиста появляется еще одна иллюзия по поводу отказоустойчивости его программы.
&lt;/p&gt;

&lt;p&gt;
Задача сброса WDT совсем нетривиальна, в каждой конкретной программе нужен свой алгоритм. В самом простом случае можно завести отдельную переменную, в которой каждая подпрограмма будет устанавливать свой бит, а отдельная подпрограмма обработки WDT будет проверять эту переменную, и только в том случае, когда все требуемые биты установлены, будет производиться очистка WDT. Но такой способ подойдет только для несложной программы с небольшим количеством состояний и с ограниченным временем пребывания в одной функции. 
&lt;/p&gt;

&lt;p&gt;
Для сложных программ требуются разработки индивидуальных алгоритмов, которые бы отслеживали не только факт выполнения каких-то функций, но также следили за тем, чтобы функции выполнялись в правильной последовательности, и чтобы время  выполнения каждой функции было в пределах допустимого. 
&lt;/p&gt;

&lt;p&gt;
Здесь хотел бы пару слов сказать про третье применение WDT, т.е. программный сброс в контроллерах младших семейств, т.к. они не имеют отдельной инструкции, вроде RESET в PIC18. В системах с повышенными требованиями по надежности программа должна иметь механизмы контроля правильности работы. Т.е. следить за стеком, за порядком действий, временами выполнения отдельных узлов, настройками периферийных модулей и т.д. При обнаружении отклонений, которые не могут быть исправлены на лету (например, замечено, что в функцию попали не через вызов CALL, а каким-то другим путем), единственный выход – это сброс. Но если в контроллерах среднего и старшего семейств есть инструкции, выполняющие сброс, то в контроллерах младшего семейства единственный способ выполнить сброс программно – это дать WDT досчитать до конца. 
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;что_делать_если_произошел_сброс_по_wdt&quot; id=&quot;что_делать_если_произошел_сброс_по_wdt&quot;&gt;Что делать, если произошел сброс по WDT&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Порядок действий при сбросе контроллера (не только по WDT) - это отдельная тема (довольно большая). Здесь опишу только в двух словах.
&lt;/p&gt;

&lt;p&gt;
Сделать нужно две вещи:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Восстановить работу (по возможности сделать это незаметным для пользователей)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Выяснить причину сброса.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

С первым все понятно: все переменные, отвечающие за текущий режим работы, за состояние процессов, какие-то текущие рабочие данные и пр. должны быть определены в неинициализируемой части RAM, т.е. в той, в которую код стартапа ничего не пишет при сбросе. Для HT-PICC такие переменные должны быть определены с квалификатором persistent:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;persistent &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; Mode;
persistent &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;  LastData&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Для MPLAB C18 они должны быть определены в секции udata, т.е. не инициализироваться при определении.
&lt;/p&gt;

&lt;p&gt;
В самом начале функции main нужно проверять причины сброса (биты POR, BOR, TO, PO, RI и пр.), и в зависимости от их состояния принимать решение о том, восстанавливать ли прежний режим работы, начинать ли все с нуля, переходить ли в аварийный режим и т.д.
&lt;/p&gt;

&lt;p&gt;
Теперь о выяснении причин сброса. Здесь многое зависит от стадии разработки устройства и его доступности. В зависимости от того, отлаживаем ли мы устройство, тестируем ли в реальных условиях или же устройство в эксплуатации, в зависимости от доступности самого устройства разработчику, от способности самого устройства известить о несанкционированном сбросе, от средств его связи с внешним миром и пр. факторов – средства выяснения причин могут быть различными. Мы не будем говорить о технике получения данных, которые нам смогут помочь установить  причину сброса, из контроллера. Это может быть что угодно: сброс данных в отдельную страницу внешней EEPROM, передача их через какой-нибудь интерфейс для протоколирования, отображение на мониторе (или ЖКИ) и т.д. Я только укажу, какие данные могут быть полезными в общем случае: 
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Содержимое стека&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Значения регистров указателей (FSR)&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

По этим данным зачастую можно сделать вывод о том, в каком месте произошел сбой. При использовании компиляторов от microchip для получения этих данных придется писать свой код startup, т.к. фирменная функция заранее предустанавливает регистры FSR (WREG14, WREG15 для MCC30). В каждом частном случае могут понадобиться дополнительные данные: текущий режим, какие-то индикаторы обработки критических участков кода и пр. Чем больше будет данных, тем проще будет анализировать причину сброса. Но нужно также искать некий компромисс, чтобы не загромождать дамп всякой ерундой, а еще, чтобы не дать пользователю запаниковать, если устройство уже в эксплуатации.
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Использовать сторожевой таймер&quot; [91568-103351] --&gt;
&lt;h3&gt;&lt;a name=&quot;два_слова_об_операторе_goto&quot; id=&quot;два_слова_об_операторе_goto&quot;&gt;Два слова об операторе GOTO&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;br/&gt;
 
&lt;p&gt;

Многие авторитетные источники строго не рекомендуют использовать оператор goto при написании программ на языках высокого уровня. Основными недостатками его применения называется сильное ухудшение читабельности текста программы и нарушение ее структурности. Да и вообще неизвестно, через что мы перепрыгиваем, выполняя goto (может быть через объявление переменной). Кроме того, часто упоминается то, что формально доказано, что любая программа, использующая goto, может быть переписана без его использования с полным сохранением функциональности. Доводы весьма убедительны, а небезызвестный Дейкстра свою статью «Доводы против оператора goto» вообще начинает с тезиса «… квалификация программистов – функция, обратно зависящая от частоты появления операторов goto в их программах».
&lt;/p&gt;

&lt;p&gt;
Сам я не люблю использовать этот оператор и испытываю большие трудности с анализом чужого кода, где он применяется. Однако, применительно к микроконтроллерам с ограниченными ресурсами я бы оправдал использование goto в некоторых случаях. 
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;выход_из_вложенных_циклов&quot; id=&quot;выход_из_вложенных_циклов&quot;&gt;Выход из вложенных циклов&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Этот пример приводится чаще всех остальных. Действительно, использование оператора goto выглядит довольно простым (и наглядным) решением для выхода из циклов вида (удобны, например, при поиске вариантов решений в многомерных массивах):
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; i &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;; i&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;j &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; j &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;15&lt;/span&gt;; j&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;...&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;goto&lt;/span&gt; BREAK_LOOP;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
BREAK_LOOP&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
В принципе, при чтении такого кода не вызывает трудностей найти метку, т.к. по смыслу операции понятно, что она внизу, после закрывающей скобки верхнего цикла. Ярые противники goto приводят два варианта альтернативного кода, позволяющего избавиться от этого оператора.
&lt;/p&gt;

&lt;p&gt;
&lt;em class=&quot;u&quot;&gt;Вариант 1&lt;/em&gt; – переписать цикл в виде функции.
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; loop_func &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt; i, j;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; i &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;; i&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;j &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; j &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;15&lt;/span&gt;; j&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
            &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;...&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;return&lt;/span&gt; ;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;em class=&quot;u&quot;&gt;Вариант 2&lt;/em&gt; – использовать переменную-флаг.
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;bool&lt;/span&gt; StopLoop &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;false&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; i &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;StopLoop; i&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;j &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; j &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;15&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;StopLoop; j&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;...&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
            StopLoop &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;true&lt;/span&gt;;
            &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
(В принципе есть еще альтернативы, но они контекстно-зависимые и в общем случае применимы не во всех случаях)
&lt;/p&gt;

&lt;p&gt;
Оба варианта полностью работоспособны, однако имеют свои небольшие недостатки, когда речь идет о работе с микроконтроллерами, имеющими дефицит ресурсов. Первый вариант не очень удачен, поскольку требует дополнительный свободный уровень стека (помним, что в PIC16 их всего 8, а в PIC18 - 32), что иногда может оказаться критичным. Второй вариант требует использования дополнительной переменной, что также существенно для контроллеров с малым ОЗУ. Т.е., применяя goto, мы имеем возможность сэкономить ресурсы контроллера. (Об экономии скорости я здесь не говорю, поскольку на фоне цикла из 10x15=150 итераций лишний вызов/возврат или две лишних проверки флага StopLoop будут несущественны).
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;стандартные_метки&quot; id=&quot;стандартные_метки&quot;&gt;«Стандартные» метки&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Лично я позволяю себе использовать метки, которые могут считаться  универсальными почти для любого случая. Таких меток немного. Типичный пример – выход из функции при обнаружении ошибки в ходе ее выполнении. Ошибку функция может обнаружить на любом этапе своего выполнения (как при проверке аргументов на правильность, так, например, и при работе с внешней периферией, и при проверке контрольных сумм и т.д.), а при выходе ей требуется освободить занимаемые ресурсы и установить флаг возникновения ошибки, так что обычный return нас может не устроить просто потому, что перед каждым return придется выполнять одну и ту же последовательность действий.
&lt;/p&gt;

&lt;p&gt;
Главное для меня – местоположение таких меток всегда однозначно (например, понятно, что метка, куда переходит функция при обнаружении ошибки, находится в конце функции), их использование безопасно и с точки зрения экономии ресурсов целесообразно (не любой алгоритм удобно программировать приемами структурного программирования, есть случаи, когда такие приемы делают код менее читабельным и более ресурсоемким).
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;оптимизация&quot; id=&quot;оптимизация&quot;&gt;Оптимизация&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Некоторые операции, такие как расчеты или работа с быстрыми сигналами, требуют оптимизации кода по скорости. В таких случаях я предпочитаю использовать операторы goto вместо &lt;code&gt;if…else&lt;/code&gt;, &lt;code&gt;break&lt;/code&gt;, &lt;code&gt;continue&lt;/code&gt; и пр. из соображений экономии времени выполнения, даже в ущерб наглядности. Такие участки кода должны быть тщательно проверены и перепроверены и детально откомментированы.
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Два слова об операторе GOTO&quot; [103352-111107] --&gt;
&lt;h3&gt;&lt;a name=&quot;атомарный_доступ&quot; id=&quot;атомарный_доступ&quot;&gt;Атомарный доступ&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;br/&gt;
 
&lt;p&gt;

Часто причиной ошибки в программах может быть непредусмотренный механизм атомарного доступа к регистрам или переменным. Самый простой пример: на 8-битном контроллере имеем 16-битную переменную, которая обрабатывается как в основной программе, так и в обработчике прерывания. Конфликты при работе с ней могут возникнуть, если во время обращения к ней из основного тела программы возникает прерывание, обработчик которого также захочет с ней поработать. Это нестрашно, когда и там и там к переменной обращаются для чтения. А вот если в одном из кусков кода (или в обоих) производится запись, то могут возникнуть проблемы.
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt; ADCValue;    &lt;span class=&quot;co1&quot;&gt;// Результат чтения АЦП (производится в&lt;/span&gt;
                          &lt;span class=&quot;co1&quot;&gt;// прервании&lt;/span&gt;
...
&lt;span class=&quot;co1&quot;&gt;// Фрагмент обработчика прерывания&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;If&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;ADIF &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; ADIE&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    ADIF     &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
    ADCValue &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;ADRESH &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; | ADRESL;  &lt;span class=&quot;co1&quot;&gt;// Читаем последнее измерение&lt;/span&gt;
    ADGO     &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;                       &lt;span class=&quot;co1&quot;&gt;// Запускаем следующее&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
...
&lt;span class=&quot;co1&quot;&gt;// Фрагмент основного тела программы&lt;/span&gt;
Data&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; ADCValue;      &lt;span class=&quot;co1&quot;&gt;// Примечание: Data[] – массив uint'ов&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Я думаю, не нужно пояснять, что произойдет, если в момент чтения &lt;span class=&quot;important&quot;&gt;ADCValue&lt;/span&gt; в основной программе (а чтение произойдет в два этапа: сначала младший байт, затем старший), произойдет прерывание по завершению измерения ADC? В двух словах: на момент начала чтения переменная содержала значение &lt;span class=&quot;important&quot;&gt;257&lt;/span&gt; (0x101). Первым считается младший байт и скопируется в младший байт элемента массива &lt;span class=&quot;important&quot;&gt;Data[i]&lt;/span&gt;; далее произойдет прерывание, и в &lt;span class=&quot;important&quot;&gt;ADCValue&lt;/span&gt; запишется вновь измеренное значение напряжения, которое с момента последнего изменение стало, например, меньше на 3 единицы младшего разряда, т.е. &lt;span class=&quot;important&quot;&gt;254&lt;/span&gt; (0x0FE). Возвращаемся из прерывания и продолжаем копировать &lt;span class=&quot;important&quot;&gt;ADCValue&lt;/span&gt; в &lt;span class=&quot;important&quot;&gt;Data[i]&lt;/span&gt;; нам осталось скопировать старший байт, а он стал равным 0. Вот и получается, что в &lt;span class=&quot;important&quot;&gt;Data[i]&lt;/span&gt; окажется значение &lt;span class=&quot;important&quot;&gt;0x001&lt;/span&gt;.
&lt;/p&gt;

&lt;p&gt;
Часто встречал у людей недопонимание методов борьбы с этим явлением. Многие программисты считают, что их спасет квалификатор &lt;code&gt;volatile&lt;/code&gt;, т.е. достаточно определить переменную, к которой есть доступ и в основном теле программ и в прерывании так:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt; ADCValue;&lt;/pre&gt;
&lt;p&gt;
- и проблема будет решена автоматически на уровне компилятора. Однако, это не так. Квалификатор &lt;code&gt;volatile&lt;/code&gt; всего лишь сообщает компилятору, что не нужно производить оптимизацию кода с участием этой переменной (подробнее о volatile читайте &lt;a href=&quot;http://wiki.pic24.ru/doku.php/osa/articles/volatile_for_chainiks&quot; class=&quot;urlextern&quot; title=&quot;http://wiki.pic24.ru/doku.php/osa/articles/volatile_for_chainiks&quot;  rel=&quot;nofollow&quot;&gt;[9&lt;/a&gt;]). А это в свою очередь даст  возможность программисту блокировать прерывания на время обращения к ней, но делать это надо вручную:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;di&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
Data&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; ADCValue;
ei&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Более детально про атомарный доступ рекомендую почитать &lt;a href=&quot;http://www.pic24.ru/doku.php/articles/mchp/c30_atomic_access&quot; class=&quot;urlextern&quot; title=&quot;http://www.pic24.ru/doku.php/articles/mchp/c30_atomic_access&quot;  rel=&quot;nofollow&quot;&gt;[6&lt;/a&gt;]. 
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Атомарный доступ&quot; [111108-115736] --&gt;
&lt;h2&gt;&lt;a name=&quot;оформление&quot; id=&quot;оформление&quot;&gt;Оформление&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#удобный_инструментарий&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Использовать удобный инструментарий&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#именование_идентификаторов&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Давать осмысленные имена идентификаторам&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#форматирование_текста&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Форматировать текст по одним и тем же правилам&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#комментирование&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Снабжать исходный текст комментариями&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Оформление&quot; [115737-116291] --&gt;
&lt;h3&gt;&lt;a name=&quot;удобный_инструментарий&quot; id=&quot;удобный_инструментарий&quot;&gt;Удобный инструментарий&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;br/&gt;
 
&lt;p&gt;

Очень важная составляющая успешного программирования – инструментарий. Применительно к написанию текста программы – это в первую очередь текстовый редактор. Если он удобный, функциональный и  имеет интуитивно понятный интерфейс, то форматирование будет производиться легко и непринужденно. Помимо самого ввода текста, редактор может обеспечить нам:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; автоматические отступы;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; замену символа табуляции пробелами;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; подсветку синтаксиса;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; контекстную подстановку;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; перекрестные ссылки внутри исходного текста или целого проекта;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; вызов компилятора для сборки проекта из редактора&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

(Рекомендую SlickEdit).
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Удобный инструментарий&quot; [116292-117486] --&gt;
&lt;h3&gt;&lt;a name=&quot;именование_идентификаторов&quot; id=&quot;именование_идентификаторов&quot;&gt;Именование идентификаторов&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;br/&gt;
 
&lt;p&gt;

Задавая имена переменным, функциям, константам, типам, перечислениям, макросам, файлам и пр., есть смысл следовать некоторым правилам:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Имя должно быть осмысленным (не i, а Counter) &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Имя должно быть содержательным (не Counter, а BitsCounter)&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

И ниже приведем кое-какие отдельные правила именования различных объектов программы.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;именование_функций&quot; id=&quot;именование_функций&quot;&gt;Именование функций&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Имя функции внутри модуля, которую предполагается вызывать из других модулей,  должно начинаться с префикса, обозначающего имя модуля. Зачем это нужно? Например, в одной программе вы используете память 24LC64, для которой определяете функцию &lt;code&gt;write_byte()&lt;/code&gt;. Потом вы написали другую программу, которая работает с внутренней EEPROM контроллера, и для нее тоже определили функцию &lt;code&gt;write_byte()&lt;/code&gt;. А потом понадобилось сделать проект, в котором будет использоваться и внешняя EEPROM, и внутренняя. Тут-то вы и столкнетесь с последствиями неправильного именования, потому что из-за одинаковых имен модули нельзя будет объединить в одной программе. Поэтому в самом начале нужно было предусмотреть префиксы имен функций (а запись байта – это операция универсальная, она есть в работе и с дисплеем, и с модемом и еще с чем угодно).
&lt;code&gt;i2c_write_byte&lt;/code&gt; – для работы с памятью 24LC64;&lt;br/&gt;
 
&lt;code&gt;eeprom_write_byte&lt;/code&gt; – для работы с внутренней EEPROM;&lt;br/&gt;
 
&lt;code&gt;lcd_write_byte&lt;/code&gt; – для работы с LCD и т.д.
&lt;/p&gt;

&lt;p&gt;
Кроме того, имя функции должно быть кратким и, по возможности, должно соответствовать системе именования: &amp;lt;модуль&amp;gt;_&amp;lt;действие&amp;gt;_&amp;lt;объект&amp;gt;[_&amp;lt;суффикс&amp;gt;] (могут быть в другом порядке, могут разделяться символом “_”), где:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&amp;lt;модуль&amp;gt;_&amp;lt;действие&amp;gt;_&amp;lt;объект&amp;gt;[_&amp;lt;суффикс&amp;gt;]&lt;/strong&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;модуль&lt;/strong&gt; – имя (или сокращенное имя) модуля, в котором определена функция;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;действие&lt;/strong&gt; – глагол, определяющий назначение функции (write, read, check, count и т.д.)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;объект&lt;/strong&gt; – существительное, определяющее параметрическую составляющую функции (byte, checksum, string, data и пр.)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;суффикс&lt;/strong&gt; – необязательное поле, отражающее какую-либо дополнительную характеристику функции (rom, timeout).&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Конечно, все имена функций под одну гребенку подвести довольно трудно, и обязательно в любой системе именования будут исключения. Но не нужно называть функции так:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:red; '&gt;Неправильные имена функций:&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
CopyOneByteFromROMToRAM();&lt;br/&gt;
 
Check();&lt;br/&gt;
 
CompareAndGetMax();&lt;br/&gt;
 
&lt;/p&gt;

&lt;p&gt;
В заключение надо сказать, что для некоторых функций можно оставить зарезервированные имена: atof(), init(), printf() и т.д.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;именование_констант&quot; id=&quot;именование_констант&quot;&gt;Именование констант&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Заглавными буквами&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Слова разделяются символом ‘_’&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Префикс в виде имени модуля или функционального назначения&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
&lt;br/&gt;
 
&lt;br/&gt;
 
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co1&quot;&gt;// Определения параметров шины i2c&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define I2C_DEV_ADDR         0xA0&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define I2C_ADDR_WIDTH         16&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;// Определения цветов RGB&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define CL_RED           0xFF0000&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define CL_YELLOW        0xFFFF00&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define CL_GREEN         0x00FF00&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;именование_типов&quot; id=&quot;именование_типов&quot;&gt;Именование типов&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Заглавными буквами&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Слова разделяются символом ‘_’&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Префикс в виде «T_», «TYPE_» и т.п.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
&lt;br/&gt;
 
&lt;br/&gt;
 
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;struct&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;long&lt;/span&gt;   seconds &lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;6&lt;/span&gt;;
    &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;long&lt;/span&gt;   minutes &lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;6&lt;/span&gt;;
    &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;long&lt;/span&gt;   hours   &lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;5&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; T_CLOCK;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;именование_переменных&quot; id=&quot;именование_переменных&quot;&gt;Именование переменных&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Слова начинаются с заглавной буквы&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Пишутся без разделителя&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;    BytesCounter;
&lt;span class=&quot;kw4&quot;&gt;signed&lt;/span&gt;   &lt;span class=&quot;kw4&quot;&gt;long&lt;/span&gt;    XSize, YSize;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;о_венгерской_нотации&quot; id=&quot;о_венгерской_нотации&quot;&gt;О «венгерской нотации»&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Некоторые используют систему именования, в которой каждой переменной приписывается префикс, показывающий ее тип. Это часто может оказаться полезным при анализе чужого (а часто и собственного) кода. Например:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Counter &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; Counter &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;40000&lt;/span&gt;; Counter&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; ...;&lt;/pre&gt;
&lt;p&gt;
В данном примере мы не можем точно сказать, правильно ли у нас использована переменная в данном цикле или нет. Переменная Counter может быть, во-первых, 8-битной; во-вторых, она может быть знаковой (т.е. всегда будет меньше 40000). И для того, чтобы определить правильность использования переменной, нам нужно найти определение переменной в файле. Но если мы изначально предусмотрели в имени переменной ее тип, то это избавит нас от проделывания лишней работы:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;wCounter &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; wCounter &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;40000&lt;/span&gt;; wCounter&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; ...;&lt;/pre&gt;
&lt;p&gt;
Теперь, глядя на эту запись, мы точно можем сказать, что здесь нет ошибки.
&lt;/p&gt;

&lt;p&gt;
Также системой именований можно предусмотреть область видимости переменной.
&lt;/p&gt;

&lt;p&gt;
Префиксы:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Префикс области видимости&lt;/div&gt;
&lt;ul&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; Без префикса – локальная или параметр функции&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;code&gt;s_&lt;/code&gt; - статическая&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;code&gt;m_&lt;/code&gt; - локальная для модуля&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;code&gt;g_&lt;/code&gt; - глобальная&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;code&gt;i_&lt;/code&gt; - Обрабатывается в прерывании&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Префикс типа&lt;/div&gt;
&lt;ul&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;code&gt;uc&lt;/code&gt; – unsigned char&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;code&gt;sc&lt;/code&gt; – signed char&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;code&gt;ui&lt;/code&gt; – unsigned int (n)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;code&gt;si&lt;/code&gt; – signed int (w)&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

И т.д.
&lt;/p&gt;

&lt;p&gt;
Имя нашей переменной может выглядеть так:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; s_ucRecCounter;&lt;/pre&gt;
&lt;p&gt;
Встречая ее в любом месте функции, мы сразу понимаем, что:

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Эта переменная предназначена для подсчета принятых байтов;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Эта переменная имеет тип unsigned char, т.е. может принимать значения 0..255;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Эта переменная статическая, т.е. сохраняет свое значение после выхода из функции.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;em&gt;&lt;strong&gt;Примечание&lt;/strong&gt;. Можно для себя иметь некоторый набор зарезервированных имен для переменных общего назначения, которые встречаются наиболее часто. Например: &lt;span class=&quot;important&quot;&gt;i, j&lt;/span&gt; – signed int; &lt;span class=&quot;important&quot;&gt;a, b&lt;/span&gt; – char; &lt;span class=&quot;important&quot;&gt;f&lt;/span&gt; - float; и т.д. Но эти имена желательно согласовывать, во-первых, с применяемыми именами в литературе, а во-вторых, внутри собственной команды разработчиков, чтобы не было так, что у одного i – signed int, а у другого i – unsigned int.&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;em class=&quot;u&quot;&gt;Преимущества венгерской нотации&lt;/em&gt;:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Предотвращает ошибки использования типов&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; В большом коде помогает не запутаться в переменных&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Упрощает чтение чужого кода&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
&lt;em class=&quot;u&quot;&gt;Недостатки венгерской нотации&lt;/em&gt;:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Ухудшают читабельность кода&lt;/div&gt;
&lt;ul&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; Выражение из 3-4 переменных уже трудно читается&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; Префикс может получиться очень длинным&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Изменение типа переменной влечет изменение ее имени во всех файлах&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Префикс не дает гарантию правильности задания типа, из-за чего может получиться ложная уверенность в корректности применения переменной:&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;strong&gt;&lt;span style='color:blue; '&gt;static&lt;/span&gt; &lt;span style='color:red; '&gt;signed&lt;/span&gt; &lt;span style='color:blue; '&gt;char&lt;/span&gt; &lt;span style='color:black; '&gt;s_&lt;/span&gt;&lt;span style='color:red; '&gt;u&lt;/span&gt;&lt;span style='color:black; '&gt;cBytesCounter;&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Именование идентификаторов&quot; [117487-127551] --&gt;
&lt;h3&gt;&lt;a name=&quot;форматирование_текста&quot; id=&quot;форматирование_текста&quot;&gt;Форматирование текста&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;br/&gt;
 
&lt;p&gt;

Очевидно, что форматирование нужно для того, чтобы программа была более наглядной. Рекомендую в качестве примера ознакомиться с документом &lt;span class=&quot;important&quot;&gt;&lt;a href=&quot;http://micrium.com/download/an2000.pdf&quot; class=&quot;urlextern&quot; title=&quot;http://micrium.com/download/an2000.pdf&quot;  rel=&quot;nofollow&quot;&gt;an_2000_rus.pdf&lt;/a&gt;&lt;/span&gt;, где описаны правила форматирования текста программ, сформулированные для сотрудников компании micrium. Не обязательно точно следовать приведенным в нем правилам, но следует посмотреть, на чем сосредоточили внимание авторы документа, и принять это на вооружение:

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Следовать одному стилю&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; При работе в команде следовать всей командой одним и тем же правилам&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Не оправдывать отсутствие форматирования нехваткой времени&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Я не буду пересказывать этот документ, а только вкратце приведу основные моменты:
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;текст_файла_должен_быть_разбит_на_секции&quot; id=&quot;текст_файла_должен_быть_разбит_на_секции&quot;&gt;Текст файла должен быть разбит на секции&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Когда мы берем в руки книгу, мы знаем, что у нее помимо самой содержательной части есть титульные данные, оглавление, выпускные данные, а часто еще алфавитный указатель, список литературы. Кроме того, мы знаем, что каждый элементы каждой из перечисленных групп находится в одном месте, а не разбросаны по всем страницам. Все эти данные упрощают восприятие книги: на титуле в двух словах сказано, о чем (иногда – для кого) эта книга, по оглавлению можно быстро найти интересующую главу, по содержанию – понять содержание и т.д.
&lt;/p&gt;

&lt;p&gt;
Для того чтобы текст программы легче читался, он также должен быть разбит на части (или секции), каждая из которых имеет свое назначение:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Заголовок файла (с информацией о назначении файла, авторе текста, используемом компиляторе, дате создания и пр.);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Хронологию изменений&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Секция включаемых файлов&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Секция определения констант&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Секция определения макросов&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Секция определения типов&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Секция определения переменных (глобальные, затем локальные)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Секция прототипов функций (глобальные, затем локальные)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Секция описания функций.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Для секций есть свои правила:

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Каждая секция должна содержать только те описания, которые ей соответствуют&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Секции должны быть едины, а не разбросаны по всему файлу. &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Каждой секции должен предшествовать хорошо заметный блок комментария с названием секции.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;горизонтальные_отступы&quot; id=&quot;горизонтальные_отступы&quot;&gt;Горизонтальные отступы&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Горизонтальные отступы служат для визуального подчеркивания структуры программы. Я каждый блок операторов делаю с отступом на 4 пробела вправо от верхнего блока.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;вертикальное_выравнивание&quot; id=&quot;вертикальное_выравнивание&quot;&gt;Вертикальное выравнивание&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
При определении переменных: типы – под типами, квалификаторы – под квалификаторами, имена переменных – под именами, атрибуты – под атрибутами. В самой программе: при присваивании блока переменных желательно выравнивать знаки «=».

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;    BytesCounter;
    &lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt;          &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;     Timer;
                    &lt;span class=&quot;kw4&quot;&gt;double&lt;/span&gt;  Price;
                    &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;   &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;p, c;
    ...
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        BytesCounter &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
        Timer        &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
        Price        &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu16&quot;&gt;1.12&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Хочу отдельно внимание обратить на то, что ‘*’, показывающая, что переменная является указателем, ставится рядом с переменной, а не с типом. Сравните две записи:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;    p, c;
и
&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;    &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;p, c;&lt;/pre&gt;
&lt;p&gt;
Первая запись может быть ошибочно прочитана так: переменные p и c имеют тип char*. На самом же деле указателем будет только p. Во второй записи это наглядно отражено.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;не_делать_в_строке_больше_символов_чем_помещается_на_одном_экране&quot; id=&quot;не_делать_в_строке_больше_символов_чем_помещается_на_одном_экране&quot;&gt;Не делать в строке больше символов, чем помещается на одном экране&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Например, если функция содержит много параметров, то имеет смысл каждый параметр писать с новой строки. Длинные выражения можно также разбивать на строки.
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt; sendto &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt; SOCKET                  s,
             &lt;span class=&quot;kw4&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;             &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;buf,
             &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;                     len,
             &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;                     flags,
             &lt;span class=&quot;kw4&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;struct&lt;/span&gt; sockaddr  &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;to,
             &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;                     tolen
           &lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Обращу ваше внимание, что при определении функции в нескольких строках правилом вертикального выравнивания стоит пользоваться не в полной мере. Попробуйте переписать эту функцию, ставя квалификаторы под квалификаторами и т.д. Он станет выглядеть довольно безобразно и совершенно нечитабельно, так что в данном случае лучше все определения начинать с одного столбца.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;одна_строка_одно_действие&quot; id=&quot;одна_строка_одно_действие&quot;&gt;Одна строка – одно действие&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;разделять_функциональные_узлы_или_конструкции_for_if_пустыми_строками&quot; id=&quot;разделять_функциональные_узлы_или_конструкции_for_if_пустыми_строками&quot;&gt;Разделять функциональные узлы или конструкции (for, if, …) пустыми строками&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;пробелы_между_операндами_и_операциями&quot; id=&quot;пробелы_между_операндами_и_операциями&quot;&gt;Пробелы между операндами и операциями&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Ниже пример форматирования по трем последним правилам:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:red; '&gt;Неправильно:&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;i&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;i&lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;;i&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;a&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;j&lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt;k&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;a&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;a&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;;&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;a&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;;a&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;;&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;strong&gt;&lt;span style='color:#00CC40; '&gt;Правильно:&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; i &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;; i&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; a&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;j &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; k&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    a&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
    a&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    a&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;;
    a&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Форматирование текста&quot; [127552-135229] --&gt;
&lt;h3&gt;&lt;a name=&quot;комментирование&quot; id=&quot;комментирование&quot;&gt;Комментирование&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;br/&gt;
 
&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;почему_не_пишут_комментарии&quot; id=&quot;почему_не_пишут_комментарии&quot;&gt;Почему не пишут комментарии&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; «Время поджимает, писать некогда»&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; «Это временный код, его не нужно комментировать»&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; «Я и так все запомню»&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; «Моя программа понятна и без комментариев»&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; «В код, кроме меня, никто не заглядывает»&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; «Комментарии делают текст пестрым и затрудняют чтение самой программы» &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; «Я потом откомментирую»&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;для_кого_пишутся_комментарии&quot; id=&quot;для_кого_пишутся_комментарии&quot;&gt;Для кого пишутся комментарии&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Комментарий программистом пишется в первую очередь для самого программиста. За отсутствие комментариев приходится платить временем. Чаще - своим, реже – чужим.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;содержание_комментариев&quot; id=&quot;содержание_комментариев&quot;&gt;Содержание комментариев&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Частенько в присланных мне программах вижу, что местами комментарий написан только для того, чтобы он там был. Такое ощущение, что человек знает, что комментарий написать надо, но не знает зачем.
&lt;/p&gt;

&lt;/div&gt;

&lt;h5&gt;&lt;a name=&quot;что_должно_быть_в_комментариях&quot; id=&quot;что_должно_быть_в_комментариях&quot;&gt;Что должно быть в комментариях:&lt;/a&gt;&lt;/h5&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Спецификация функций: &lt;/div&gt;
&lt;ul&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; что делает;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; входные и выходные параметры (типы и краткое пояснение);&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Назначение объявляемой переменной, определяемой константы, типа, макроса;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Краткое, емкое, безызбыточное описание действия или пояснение к нему;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Пометки об изменениях: &lt;/div&gt;
&lt;ul&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; версия (или дата) и номер пометки. &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; Причины и описание изменения желательно держать в отдельном месте, т.к., во-первых, текст описания причин изменений и производимых действий может получиться громоздким и будет засорять основной текст программы, а во-вторых, одна причина может повлечь за собой изменения нескольких участков программы. &lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Указание отладочных узлов и временных конструкций&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Пример спецификации функции:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;coMULTI&quot;&gt;/*************************************************************
 *
 *  Function:       rs_buf_delete
 *
 *------------------------------------------------------------
 *
 *  description:    Удаляем N байт из буфера
 *
 *  parameters:     uchar N – количество удаляемых байтов
 *
 *  on return:      void
 *
 *************************************************************/&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; rs_buf_delete &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;uchar N&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    ...&lt;/pre&gt;
&lt;p&gt;
Пример пометок об изменениях:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;coMULTI&quot;&gt;/****************************************************
 * Список изменений
 * ...
 * Версия 1.6 (22.10.2009):
 *     1. ...
 *     2. ...
 *     ...
 *     8. Иванов И.И., 17.09.2009: В функции
 *        rs_buf_delete добавлена проверка
 *        входного аргумента на 0
 * ...
 ***************************************************/&lt;/span&gt;
...
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; rs_buf_delete &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;signed&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; N&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;// *1.6-8* if (N &amp;lt; 0) return;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;N &lt;span class=&quot;sy1&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;return&lt;/span&gt;;          &lt;span class=&quot;co1&quot;&gt;// *1.6-8*&lt;/span&gt;
    ...&lt;/pre&gt;
&lt;p&gt;
Пример указания отладочного узла:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; rs_buf_delete &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;signed&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; N&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;N &lt;span class=&quot;sy1&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;return&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;coMULTI&quot;&gt;/*D*/&lt;/span&gt;  DebugCounter++;
    &lt;span class=&quot;coMULTI&quot;&gt;/*D*/&lt;/span&gt;  PIN_DEBUG &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
    &lt;span class=&quot;coMULTI&quot;&gt;/*D*/&lt;/span&gt;  delay_ms&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;coMULTI&quot;&gt;/*D*/&lt;/span&gt;  PIN_DEBUG &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
    ...&lt;/pre&gt;
&lt;/div&gt;

&lt;h5&gt;&lt;a name=&quot;чего_в_комментариях_быть_не_должно&quot; id=&quot;чего_в_комментариях_быть_не_должно&quot;&gt;Чего в комментариях быть не должно:&lt;/a&gt;&lt;/h5&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Эмоций&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    RB0 &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;         &lt;span class=&quot;co1&quot;&gt;// Без этой хрени не работает.&lt;/span&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Описания устаревших действий&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;BufSize &lt;span class=&quot;sy1&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;co1&quot;&gt;// Буфер не пуст, флаг разрешения&lt;/span&gt;
                     &lt;span class=&quot;co1&quot;&gt;// вывода установлен&lt;/span&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Дублирования действия&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    BufSize &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;     &lt;span class=&quot;co1&quot;&gt;// Обнуляем переменную, показывающую&lt;/span&gt;
                     &lt;span class=&quot;co1&quot;&gt;// размер буфера&lt;/span&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Бесполезной информации&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;N &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;      &lt;span class=&quot;co1&quot;&gt;// Лучший вариант сравнения&lt;/span&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Непонятных сокращений и жаргона:&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    A &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; X &lt;span class=&quot;sy2&quot;&gt;/&lt;/span&gt; Y &lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt; Z;   &lt;span class=&quot;co1&quot;&gt;// В (*1*), т.н.у., обход&lt;/span&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Ложной или вводящей в заблуждение информации:&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;timer &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; V &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;co1&quot;&gt;// Если за 100мс напряжение&lt;/span&gt;
                               &lt;span class=&quot;co1&quot;&gt;// стало выше 10 вольт&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;расположение_комментариев&quot; id=&quot;расположение_комментариев&quot;&gt;Расположение комментариев&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Не следует мешать код и текст комментариев в одну кучу. Например, так:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:red; '&gt;Неправильный подход:&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;coMULTI&quot;&gt;/* Инициализация портов*/&lt;/span&gt;
PIN_DATA &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
PIN_CLC &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;coMULTI&quot;&gt;/* Очистка буфера */&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; i &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; BUF_SIZE; i&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; Buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;coMULTI&quot;&gt;/*Ожидание данных */&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;ReadData&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    ...&lt;/pre&gt;
&lt;p&gt;
Комментарии нужно писать так, чтобы они не сливались с кодом. Один из вариантов – выносить их на поля, выравнивая по вертикали.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:#00CC40; '&gt;Правильный подход:&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;PIN_DATA &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;                               &lt;span class=&quot;coMULTI&quot;&gt;/* Инициализация портов    */&lt;/span&gt;
PIN_CLC &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; i &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; BUF_SIZE; i&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; Buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;  &lt;span class=&quot;coMULTI&quot;&gt;/* Очистка буфера          */&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;ReadData&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;                          &lt;span class=&quot;coMULTI&quot;&gt;/* Ожидание данных         */&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    ...&lt;/pre&gt;
&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;многострочные_комментарии&quot; id=&quot;многострочные_комментарии&quot;&gt;Многострочные комментарии&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
При написании многострочного комментария (например, описывающего функцию), нужно, чтобы каждая строка начиналась с символа, обозначающего, что это комментарий. 
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;span style='color:red; '&gt;Неправильный подход:&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;coMULTI&quot;&gt;/* Эта функция считывает начальные установки из EEPROM,
   проверяя контрольную сумму. Если контрольная сумма не
   сошлась, то будут приняты установки по умолчанию. */&lt;/span&gt;
   &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; i &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; BUF_SIZE; i&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; ...;&lt;/pre&gt;
&lt;p&gt;
&lt;strong&gt;&lt;span style='color:#00CC40; '&gt;Правильные подходы:&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
(не очень удобный вариант: из-за наличия правого ограничителя такой комментарий утомительно редактировать)
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;coMULTI&quot;&gt;/* Эта функция считывает начальные установки из EEPROM,    */&lt;/span&gt;
&lt;span class=&quot;coMULTI&quot;&gt;/* проверяя контрольную сумму. Если контрольная сумма не   */&lt;/span&gt;
&lt;span class=&quot;coMULTI&quot;&gt;/* сошлась, то будут приняты установки по умолчанию.       */&lt;/span&gt;
   &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; i &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; BUF_SIZE; i&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; ...;&lt;/pre&gt;
&lt;p&gt;
(альтернативные варианты)
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;coMULTI&quot;&gt;/*
 * Эта функция считывает начальные установки из EEPROM,
 * проверяя контрольную сумму. Если контрольная сумма не
 * сошлась, то будут приняты установки по умолчанию.
 */&lt;/span&gt;
   &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; i &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; BUF_SIZE; i&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; ...;&lt;/pre&gt;&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co1&quot;&gt;// Эта функция считывает начальные установки из EEPROM,&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;// проверяя контрольную сумму. Если контрольная сумма не&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;// сошлась, то будут приняты установки по умолчанию.&lt;/span&gt;
   &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; i &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; BUF_SIZE; i&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; ...;&lt;/pre&gt;
&lt;p&gt;

Часто удобно пользоваться горизонтальными разделителями для визуального отделения логически разных блоков в тексте программы:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co1&quot;&gt;//--------------------------------------------------&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
или
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;coMULTI&quot;&gt;/**************************************************/&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;содержательная_часть_комментария&quot; id=&quot;содержательная_часть_комментария&quot;&gt;Содержательная часть комментария&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Вспомним, что мы говорили про именование идентификаторов и использование именованных констант. Рассмотрим пример:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;result&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;           &lt;span class=&quot;co1&quot;&gt;// Проверяем состояние модема&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt;               &lt;span class=&quot;co1&quot;&gt;// Модем выключен&lt;/span&gt;
         …
         &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt;               &lt;span class=&quot;co1&quot;&gt;// Модем готов к работе&lt;/span&gt;
         …
         &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt;               &lt;span class=&quot;co1&quot;&gt;// Модем производит дозвон&lt;/span&gt;
         …
         &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;
    …
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Если бы использовали осмысленные имена (и переменной и констант), то сам фрагмент программы оказался бы понятным и без комментариев. 
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;ModemState&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;case&lt;/span&gt; MODEM_OFF&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt;
         …
         &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;case&lt;/span&gt; MODEM_READY&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt;
         …
         &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;case&lt;/span&gt; MODEM_CALLING&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt;
         …
         &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;
    …
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Тем самым у нас появляется возможность комментарием пояснить какой-нибудь нюанс какой-то ситуации или какого-то действия.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;формулировка&quot; id=&quot;формулировка&quot;&gt;Формулировка&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
&lt;strong&gt;Совет&lt;/strong&gt;: формулируя комментарий, всегда представляйте, как будто комментарий читает другой человек. Это поможет сделать формулировку более четкой.
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Комментирование&quot; [135230-145837] --&gt;
&lt;h1&gt;&lt;a name=&quot;отладка_и_тестирование&quot; id=&quot;отладка_и_тестирование&quot;&gt;Отладка и тестирование&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
Отладка, как и тестирование, – неотъемлемая часть разработки. Я отвел для этих понятий один общий раздел, т.к. обычно, эти этапы производятся параллельно. Надо сказать, что эти два этапа дополняют друг друга. 
&lt;/p&gt;

&lt;p&gt;
Рассмотрим следующие моменты:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#инструменты&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Инструменты&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#резерв_по_ресурсам&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Резерв по ресурсам&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#заглушки_и_тестеры&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Заглушки и тестеры&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#предупреждения_при_компиляции&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Предупреждения при компиляции&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#вывод_отладочной_информации&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Вывод отладочной информации&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#блокировка_вывода_отладочной_информации&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Блокировка вывода отладочной информации&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#резервное_копирование&quot; title=&quot;osa:articles:encoding_without_errors &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Резервное копирование&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Отладка и тестирование&quot; [145838-146725] --&gt;
&lt;h2&gt;&lt;a name=&quot;инструменты&quot; id=&quot;инструменты&quot;&gt;Инструменты&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
В арсенале программиста может быть довольно мощный инструментарий: симулятор, внутрисхемный отладчик, лог. анализатор, осциллограф и т.д. В целом, неизвестно, сколько  ошибок нужно будет обнаружить и исправить, поэтому процесс отладки и тестирования может сильно затянуться. И здесь наличие хорошего инструментария и умение с ним работать поможет сэкономить массу времени и сделает сам процесс более легким. Поэтому не нужно скупиться на средства отладки.
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Инструменты&quot; [146726-147608] --&gt;
&lt;h2&gt;&lt;a name=&quot;резерв_по_ресурсам&quot; id=&quot;резерв_по_ресурсам&quot;&gt;Резерв по ресурсам&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Желательно еще на этапе планирования предусмотреть возможность взять для отладки контроллер с запасом по ресурсам. Для чего они могут понадобиться? В процессе тестирования или отладки программы нам может понадобится контроль хода выполнения или каких-то внутренних переменных. Для этого нам понадобится запас:

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; периферийных возможностей&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; памяти для размещения отладочного кода&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; резерв по скорости&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;запас_по_периферии&quot; id=&quot;запас_по_периферии&quot;&gt;Запас по периферии&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;/div&gt;

&lt;h5&gt;&lt;a name=&quot;внутренняя_периферия_контроллера&quot; id=&quot;внутренняя_периферия_контроллера&quot;&gt;Внутренняя периферия контроллера&lt;/a&gt;&lt;/h5&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Здесь масса вариантов. Как минимум – иметь свободные выводы контроллера для возможности устанавливать на них логические уровни, соответствующие состоянию программы. Если в устройстве не планируется использовать модуль USART, то было бы удобно оставить свободными выводы контроллера, на которые этот модуль работает. Тогда отладочную информацию можно будет сливать, используя аппаратные возможности контроллера. Если использование USART все же планируется, а отладочную информацию все равно хочется слать через RS-232 в компьютер, то можно пожертвовать системным временем и сделать функцию вывода программно на любом свободном выводе контроллера (функция ввода нужна не так часто, а функция вывода реализуется довольно просто). Зачастую есть возможность скидывать отладочные данные в общем потоке данных (по UART, по радиоканалу).
&lt;/p&gt;

&lt;p&gt;
Также для отладки и тестирования может понадобиться аппаратный таймер для определения скорости выполнения некоторых участков или времени реакции на событие. Так что, если есть возможность на этапе планирования зарезервировать один аппаратный таймер для нужд отладки, то лучше сделать это.
&lt;/p&gt;

&lt;/div&gt;

&lt;h5&gt;&lt;a name=&quot;внешняя_периферия&quot; id=&quot;внешняя_периферия&quot;&gt;Внешняя периферия&lt;/a&gt;&lt;/h5&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Например, если используется LDC, то можно предусмотреть какой-то сегмент для вывода отладочной информации. В EEPROM можно выделить отдельную страницу, куда сохранять состояние контроллера после сброса, если при запуске выясняется, что сброс был аварийным (например, туда можно записать содержимое стека, пускай сам указатель и не сохранился). 
&lt;/p&gt;

&lt;p&gt;
Также можно на плате предусмотреть дополнительные кнопки, дополнительные светодиоды.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;память_для_размещения_отладочного_кода&quot; id=&quot;память_для_размещения_отладочного_кода&quot;&gt;Память для размещения отладочного кода&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Здесь все понятно: отладочный код занимает место в ROM и часто требует каких-то ячеек RAM-памяти. И будет очень обидно, если из-за отладочных функций и макросов не будет умещаться основная программа.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;резерв_скорости&quot; id=&quot;резерв_скорости&quot;&gt;Резерв скорости&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Естественно, подпрограммы вывода отладочной информации не выполняются мгновенно. Может получиться так, что из-за ее вывода программа будет просто не успевать отрабатывать свой алгоритм. Это нужно учитывать как при выборе тактовой частоты контроллера, так и при написании отладочных подпрограмм.
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Резерв по ресурсам&quot; [147609-152429] --&gt;
&lt;h2&gt;&lt;a name=&quot;заглушки_и_тестеры&quot; id=&quot;заглушки_и_тестеры&quot;&gt;Заглушки и тестеры&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;функции-заглушки&quot; id=&quot;функции-заглушки&quot;&gt;Функции-заглушки&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
В процессе тестирования могут пригодиться так называемые заглушки – пустотелые функции, соответствующие спецификации, но подменяющие вычисления (или результаты какой-то другой операции) заведомо правильным результатом. Когда это может оказаться полезным:

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Когда при отладке нужны результаты работы еще ненаписанных подпрограмм.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; При тестировании в симуляторе, когда нет возможности работать с какими-то внешними устройствами (например, GPS);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; При тестировании кода, содержащего долго выполняющиеся функции, не участвующие в тесте подпрограммы (типичный пример – задержки)&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;функции-тестеры&quot; id=&quot;функции-тестеры&quot;&gt;Функции-тестеры&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
Когда мы пишем новую функцию, мы хотим убедиться в ее работоспособности. Есть смысл писать небольшие функции, которые будут проводить тестирование новой, многократно запуская ее, передавая ей тестовые параметры. Дальнейший результат можно, в зависимости от назначения функции, наблюдать визуально или сравнивать с шаблоном. Такие функции могут формировать для нашей программы последовательность входных параметров, которая может возникнуть в реальной обстановке. Это также может оказаться особенно полезным, когда программа отлаживается или тестируется в симуляторе. Важно, чтобы при тестировании функция запускалась именно на рабочем контроллере или его аналоге (а не на Builder C++, Visual C++ и пр., на которых можно отлаживать только макет функции), т.к. каждый компилятор имеет свои особенности (типы данных, автоматическое преобразование типов, работа со стеком и пр.); каждый процессор – свои: архитектура (Гарвадрская и Неймоновская), объем доступной памяти и т.д.
&lt;/p&gt;

&lt;p&gt;
Самое сложное место в этой схеме – шаблон. Ведь мы писали новую функцию именно для того, чтобы она нам этот результат вычисляла. Где брать шаблон для сравнения? Есть три пути:

&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; В некоторых случаях есть возможность результат для сравнения формировать сторонней функцией. Например, известно, что функция printf из-за своей универсальности довольно громоздкая, и далеко не в каждом случае программист может позволить себе ее использовать, отняв у контроллера чуть ли не половину памяти и несколько тысяч тактов. Поэтому частое явление, когда программист пишет свою mini-printf, удовлетворяющую требованиям спецификации конкретной программы. Для проверки работоспособности своей функции он на этапе тестирования может пользоваться результатами работы встроенной функции printf. Причем эта функция может тестироваться в автоматическом режиме. Такой же способ подойдет для проверки математических функций.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Входные параметры и результат вбивать вручную. Довольно трудоемкий процесс, поэтому в первую очередь желательно тестировать граничные значения.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Третий вариант – брать данные извне (например, в автоматическом режиме с PC)&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Заглушки и тестеры&quot; [152430-157458] --&gt;
&lt;h2&gt;&lt;a name=&quot;предупреждения_при_компиляции&quot; id=&quot;предупреждения_при_компиляции&quot;&gt;Предупреждения при компиляции&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Серьезная ошибка, которую допускают программисты – это игнорирование предупреждений компилятора. Более того, к ним относятся как к назойливым мухам: «Ну, наконец-то собрал программу. Только своими дурацкими предупреждениями забил всю статистику, что ничего не прочтешь!» А между тем, компилятор ругается не просто так. Тут он нам не враг, а помощник. Он сообщает, что в программе есть двояко толкуемые конструкции, которые он транслировал на свое усмотрение. Дело в том, что ошибку можно совершить, даже написав все правильно с точки зрения языка. Например, в программе могут встретиться выражения вида:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;a &lt;span class=&quot;sy1&quot;&gt;==&lt;/span&gt; b&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; ...;
&lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;a &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; b&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;  ...; &lt;span class=&quot;co1&quot;&gt;// Warning: Assignment inside relational expression&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
В зависимости от того, что мы хотим сделать, мы можем применить первое или второе выражение в скобках оператора if. В первом выражении мы проверяем переменные a и b на равенство, в то время как во втором мы проверяем переменную a на 0 после присваивания ей значения из переменной b. В общем случае второй записью пользоваться не рекомендуется, т.к. гораздо нагляднее:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;a &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; b;
&lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;a&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; ...;&lt;/pre&gt;
&lt;p&gt;
Потому-то компилятор и выдает нам предупреждение, что в выражении отношения имеется оператор присваивания, давая тем самым понять, что, возможно, мы имели в виду оператор отношения “==”. (Иногда, все же, есть смысл использовать вторую запись, когда, например, код критичен ко времени выполнения.)
&lt;/p&gt;

&lt;p&gt;
Также частым следствием предупреждения компилятора является неаккуратное отношение к приведению типов. Например, в том же операторе сравнения:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;signed&lt;/span&gt;   &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;  a;
&lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;  b;
…
&lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;a &lt;span class=&quot;sy1&quot;&gt;&amp;gt;&lt;/span&gt; b&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; …;     &lt;span class=&quot;co1&quot;&gt;// Warning: signed and unsigned comparison&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
мы получим предупреждение, если одна переменная знаковая, а вторая – нет. Это предупреждение говорит не о том, что «осторожно! Может случиться коллизия», а о том, что программистом неправильно выбраны типы. Если же программист уверен в правильности, то он должен сделать приведение типов вручную:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;signed&lt;/span&gt;   &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;  a;
&lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;  b;
...
&lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;signed&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; a &lt;span class=&quot;sy1&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;signed&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; b&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; ...;&lt;/pre&gt;
&lt;p&gt;
Тем самым давая компилятору понять, что программист в курсе неправильно выбранных типов. Обратим внимание на то, что приведение типов производится к знаковому типу большей разрядности.
&lt;/p&gt;

&lt;p&gt;
(Вообще же, повторюсь, с выражениями, где участвуют переменные различных типов, нужно быть осторожнее. Например, MPLAB C18 не выдаст предупреждения на наш пример, что в свою очередь приведет к неправильному поведению программы, если a будет иметь отрицательное значение.)
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;что_делать_если_компилятор_выдал_предупреждение&quot; id=&quot;что_делать_если_компилятор_выдал_предупреждение&quot;&gt;Что делать, если компилятор выдал предупреждение?&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;
В первую очередь нужно определиться, почему он выдал предупреждение. Если его текст не понятен, то нужно обратиться к документации на компилятор и прочитать больше информации (часто в описании предупреждений приводятся наиболее частые причины их возникновения). Во вторую – привести код к однозначно интерпретируемому виду. 
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Предупреждения при компиляции&quot; [157459-162554] --&gt;
&lt;h2&gt;&lt;a name=&quot;вывод_отладочной_информации&quot; id=&quot;вывод_отладочной_информации&quot;&gt;Вывод отладочной информации&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
При формировании отладочной информации нужно помнить, что информация об ошибках, выводимая для пользователя, и информация о поведении программы, выводимая для программиста, - это абсолютно разные вещи. Причем как по содержанию, так и по способу информирования. 
&lt;/p&gt;

&lt;p&gt;
Не нужно пользователя снабжать шестнадцатеричными кодами на дисплее, а по телефону потом разъяснять, что это «переменная режима работы приняла странное значение, сейчас подумаю, почему такое произошло». Также не нужно в конечном варианте программы оставлять всяческие вспыхивания светодиодов и взвизгивания пьезодинамика, которые при отладке говорили программисту о каком-то поведении программы или о ходе вычислений (а потом по телефону объяснять, что если лампочка мигнула 3 раза по 50 мс, то это не сошлась контрольная сумма). Т.е. вся отладочная информация, которая будет выводиться для программиста, должна быть скрыта от пользователя (запись в EEPROM, передача кода ошибки на центральный пульт, если такой есть). 
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Вывод отладочной информации&quot; [162555-164420] --&gt;
&lt;h2&gt;&lt;a name=&quot;блокировка_вывода_отладочной_информации&quot; id=&quot;блокировка_вывода_отладочной_информации&quot;&gt;Блокировка вывода отладочной информации&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
Нужно помнить, что возможность вывода отладочной информации, скорее всего, придется оставить в контроллере на всю жизнь. Иногда и отлаженные и переотлаженные программы могут содержать ошибки. Тем не менее, нужно иметь возможность отключать:

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; вывод отладочной информации (как всей скопом, так и отдельных узлов);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; заглушки&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; тестеры (их полезно оставлять в тексте программы на случай проявлений ошибок в период эксплуатации) &lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Блокировать можно двумя способами:

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; на этапе компиляции, блокируя все отладочные узлы условными директивами компиляции:&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;co2&quot;&gt;#define DEBUG_ENABLE&lt;/span&gt;
    ...
    &lt;span class=&quot;coMULTI&quot;&gt;/*D*/&lt;/span&gt; &lt;span class=&quot;co2&quot;&gt;#ifdef DEBUG_ENABLE&lt;/span&gt;
    &lt;span class=&quot;coMULTI&quot;&gt;/*D*/&lt;/span&gt; PIN_DEBUG &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
    &lt;span class=&quot;coMULTI&quot;&gt;/*D*/&lt;/span&gt; &lt;span class=&quot;co2&quot;&gt;#endif&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;

(тогда, закомментировав определение DEBUG_ENABLE, мы удаляем из кода всю отладочную составляющую). Такой способ экономит ресурсы контроллера.

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; на программно-аппаратном уровне (например, контролируя состояние порта):&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;PIN_DEBUG_ENABLE&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; PIN_DEBUG &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
(тогда мы можем включать/отключать работу отладочных подпрограмм в ходе выполнения.) Такой способ требует ресурсы, но позволяет отлаживать устройство в реальных условиях.
&lt;/p&gt;

&lt;p&gt;
То же самое касается блокировки функций-заглушек и функций-проверок. Например:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define DUMMY_GPS_IN&lt;/span&gt;
...
&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt; GetGPSData &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#ifdef DUMMY_GPS_IN&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; test_string&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; “$GPRMC,A,&lt;span class=&quot;nu0&quot;&gt;123456&lt;/span&gt;,…”;
    &lt;span class=&quot;kw1&quot;&gt;return&lt;/span&gt; test_string;
&lt;span class=&quot;co2&quot;&gt;#else&lt;/span&gt;
    &lt;span class=&quot;coMULTI&quot;&gt;/* Здесь код для работы с реальными данными от GPS */&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#endif&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
В последнем примере иногда можно обойтись и без #else, т.к. внутри этого блока окажется довольно большой код, что неудобно. 
Разумеется, такие блоки в коде следует визуально выделять.
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Блокировка вывода отладочной информации&quot; [164421-167172] --&gt;
&lt;h2&gt;&lt;a name=&quot;резервное_копирование&quot; id=&quot;резервное_копирование&quot;&gt;Резервное копирование&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;
По ходу сопровождения (а иногда и разработки) программы нужно сохранять резервные копии, отмечая даты, номера версий, сделанные изменения и версию компилятора. Причем версию следует сохранять целиком, вместе с проектными файлами и HEX&amp;#039;ом. Бывает так, что незначительное изменение приводит к потере работоспособности кода, и, не имея резервных копий, зачастую трудно установить, какая именно из последних модификаций привела к таким последствиям. 
&lt;/p&gt;

&lt;p&gt;
Однако, сохранять целиком всю версию при каждой небольшой модификации довольно накладно (и по времени, и по трудоемкости). Поэтому для решения подобных задач удобно пользоваться специальными инструментами: системами контроля версий (VCS - version control systems). 
&lt;/p&gt;

&lt;p&gt;
Системы контроля версий обеспечивают два основных процесса: 
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; взаимодействие группы разработчиков, работающих над одним проектом &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; сохранение текущих &amp;quot;срезов&amp;quot; проекта в базе данных.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Существуют различные реализации VCS: коммерческие и бесплатные, использующие центральный сервер и с распределенным хранением. Одна из самых распространенных - бесплатная система управления версий &lt;strong&gt;&lt;a href=&quot;http://ru.wikipedia.org/wiki/Subversion&quot; class=&quot;urlextern&quot; title=&quot;http://ru.wikipedia.org/wiki/Subversion&quot;  rel=&quot;nofollow&quot;&gt;Subversion&lt;/a&gt;&lt;/strong&gt; с открытым исходным кодом. Именно ее можно порекомендовать для начального ознакомления с VCS.
&lt;/p&gt;

&lt;p&gt;
Subversion (или SVN) является клиент-серверной системой. База данных проекта или репозиторий хранится на сервере, а разработчик работает с локальной копией проекта, которую &amp;quot;отдает&amp;quot; ему приложение-клиент. Поработав с локальной копией, разработчик сохраняет на сервере изменения проекта - таким образом обеспечивается безопасная работа в группе и полный контроль над ходом работы. В любой момент можно извлечь один из &amp;quot;срезов&amp;quot; (в терминологии SVN - ревизий) проекта и посмотреть всю историю изменений, а также произвести сравнение различных версий.
Наиболее популярная программа клиент Subversion для Windows - &lt;strong&gt;&lt;a href=&quot;http://tortoisesvn.tigris.org/&quot; class=&quot;urlextern&quot; title=&quot;http://tortoisesvn.tigris.org/&quot;  rel=&quot;nofollow&quot;&gt;TortoiseSVN&lt;/a&gt;&lt;/strong&gt;.
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Резервное копирование&quot; [167173-170589] --&gt;
&lt;h1&gt;&lt;a name=&quot;список_литературы&quot; id=&quot;список_литературы&quot;&gt;Список литературы&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Э. Йодан «Структурное проектирование и конструирование программ», М. Мир, 1979&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Б. Керниган, Р. Пайк «Практика программирования»&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; А. Голуб «Веревка достаточной длины, чтобы… выстрелить себе в ногу»&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://micrium.com/download/an2000.pdf&quot; class=&quot;urlextern&quot; title=&quot;http://micrium.com/download/an2000.pdf&quot;  rel=&quot;nofollow&quot;&gt;http://micrium.com/download/an2000.pdf&lt;/a&gt; - правила форматирования текста программ от micrium (перевод на русский &lt;a href=&quot;http://andromega.narod.ru/doc/micrium_an_2000_rus.pdf&quot; class=&quot;urlextern&quot; title=&quot;http://andromega.narod.ru/doc/micrium_an_2000_rus.pdf&quot;  rel=&quot;nofollow&quot;&gt;http://andromega.narod.ru/doc/micrium_an_2000_rus.pdf&lt;/a&gt;)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; С. Макконнелл «Совершенный код», Русская редакция, 2005&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://www.pic24.ru/doku.php/articles/mchp/c30_atomic_access&quot; class=&quot;urlextern&quot; title=&quot;http://www.pic24.ru/doku.php/articles/mchp/c30_atomic_access&quot;  rel=&quot;nofollow&quot;&gt;http://www.pic24.ru/doku.php/articles/mchp/c30_atomic_access&lt;/a&gt; - статья об атомарном доступе&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://www.devdoc.ru/index.php/content/view/debugging_p1.htm&quot; class=&quot;urlextern&quot; title=&quot;http://www.devdoc.ru/index.php/content/view/debugging_p1.htm&quot;  rel=&quot;nofollow&quot;&gt;http://www.devdoc.ru/index.php/content/view/debugging_p1.htm&lt;/a&gt; - статья, посвященная отладке и тестированию приложений C++. Хоть и не имеет отношения к контроллерам, содержит много полезных советов.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Ван Тассел Д. «Стиль, разработка, эффективность, отладка и испытание программ», М. «Мир», 1981&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://wiki.pic24.ru/doku.php/osa/articles/volatile_for_chainiks&quot; class=&quot;urlextern&quot; title=&quot;http://wiki.pic24.ru/doku.php/osa/articles/volatile_for_chainiks&quot;  rel=&quot;nofollow&quot;&gt;http://wiki.pic24.ru/doku.php/osa/articles/volatile_for_chainiks&lt;/a&gt; - статья &amp;quot;volatile для чайников&amp;quot;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;p&gt;

Виктор Тимофеев, ноябрь 2009
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;mailto:&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot; class=&quot;mail JSnocheck&quot; title=&quot;&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot;&gt;osa@pic24.ru&lt;/a&gt;

&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Список литературы&quot; [170590-] --&gt;</description>
    </item>
    <item rdf:about="http://www.pic24.ru/doku.php/osa/articles/intro?rev=1365513233">
        <dc:format>text/html</dc:format>
        <dc:date>2013-04-09T17:13:53+03:00</dc:date>
        <title>Статьи Виктора Тимофеева</title>
        <link>http://www.pic24.ru/doku.php/osa/articles/intro?rev=1365513233</link>
        <description>


&lt;h1&gt;&lt;a name=&quot;статьи_виктора_тимофеева&quot; id=&quot;статьи_виктора_тимофеева&quot;&gt;Статьи Виктора Тимофеева&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/vga_terminal&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:vga_terminal&quot;&gt;&amp;quot;Текстовый VGA-терминал на PIC18&amp;quot;&lt;/a&gt;&lt;/strong&gt; &lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;em&gt;Рассмотрена возможность применения контроллеров PIC18 для генерации сигналов, управляющих VGA-дисплеем. В качестве примера приведена программа-терминал для отображения текста.&lt;/em&gt;

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/pk2_osa_piano&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:pk2_osa_piano&quot;&gt;&amp;quot;Пианино&amp;quot; на ОСРВ&lt;/a&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;em&gt;В данной статье рассматривается возможность обработки сенсорной клавиатуры с применением АЦП. В качестве примера приведена разработка программы 8-голосого сенсорного 3-октавного Пианино.&lt;/em&gt;

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/pk2_osa_lights&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:pk2_osa_lights&quot;&gt;&amp;quot;Бегущие огни&amp;quot; на ОСРВ&lt;/a&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;em&gt; Здесь подробно рассмотрен пример разработки программы на PIC-контроллере с использованием ОСРВ OSA. Пример очень простой и подойдет даже для начинающего. Правда, требуются навыки программирования на языке Си. В качестве аппаратной базы выбраны демо-платы из комплекта PicKit2 на базе контроллеров PIC16F886, PIC16F887 и  PIC16F690.&lt;/em&gt;

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Программирование микроконтроллеров с использованием ОСРВ OSA&lt;/a&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;em&gt;Данная статья является ответом на большинство вопросов по ОСРВ OSA, присланных мне по почте. В ней обобщены часто совершаемые ошибки и часто задаваемые вопросы, а также даны некоторые рекомендации по оптимизации программ, написанных с использованием OSA.&lt;/em&gt;

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/encoding_without_errors&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:encoding_without_errors&quot;&gt;Как писать программы без ошибок&lt;/a&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
&lt;em&gt;Практическое пособие для программистов разработчиков встраиваемых систем с примерами на языке Си.&lt;/em&gt;

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/scl&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:scl&quot;&gt;Язык описания скриптов SCL&lt;/a&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
&lt;em&gt;Фирменное описание языка SCL отсутствует, так что я предпринял попытку собрать результаты своих исследований в одном пособии. В статье также приведени примеры скриптов.&lt;/em&gt;

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/volatile_for_chainiks&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:volatile_for_chainiks&quot;&gt;volatile для &amp;quot;чайников&amp;quot;&lt;/a&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
&lt;em&gt;Статья о квалификаторе volatile, которым при программировании встраиваемых систем часто пренебрегают, даже не догадываясь о том, что в программе появляются уязвимые места, приводящие к редким и совершенно неуловимым сбоям.&lt;/em&gt;

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/modules&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:modules&quot;&gt;Как оформлять модули&lt;/a&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
&lt;em&gt;Краткое пособие по оформлению модулей на языке Си для начинающих.&lt;/em&gt;

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/mpasm_formatting&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:mpasm_formatting&quot;&gt;Как оформлять программы на ассемблере&lt;/a&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_misbeliefs&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_misbeliefs&quot;&gt;RTOS: распространенные заблуждения&lt;/a&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
&lt;em&gt;Шесть распространенных заблуждений о применении RTOS в малоресурсных МК&lt;/em&gt;

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; По просьбам: &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/wdt&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:wdt&quot;&gt;5.4 Сторожевой таймер (WDT)&lt;/a&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
&lt;em&gt;Заготовка к главе книги &amp;quot;Отказоустойчивое ПО для МК&amp;quot;. Писалась давно; на сегодня, к сожалению, не дописана (и вряд ли будет), но основы почерпнуть можно.&lt;/em&gt;
&lt;/p&gt;

&lt;/div&gt;
</description>
    </item>
    <item rdf:about="http://www.pic24.ru/doku.php/osa/articles/modules?rev=1279523651">
        <dc:format>text/html</dc:format>
        <dc:date>2010-07-19T11:14:11+03:00</dc:date>
        <title>Как оформлять модули</title>
        <link>http://www.pic24.ru/doku.php/osa/articles/modules?rev=1279523651</link>
        <description>


&lt;h2&gt;&lt;a name=&quot;как_оформлять_модули&quot; id=&quot;как_оформлять_модули&quot;&gt;Как оформлять модули&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

&lt;a href=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/c_modules.pdf&quot; class=&quot;media mediafile mf_pdf&quot; title=&quot;osa:articles:c_modules.pdf&quot;&gt;Скачать в PDF-формате&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Виктор Тимофеев, июль, 2010
&lt;a href=&quot;mailto:&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot; class=&quot;mail JSnocheck&quot; title=&quot;&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot;&gt;osa@pic24.ru&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Как оформлять модули&quot; [1-188] --&gt;
&lt;h3&gt;&lt;a name=&quot;вступление&quot; id=&quot;вступление&quot;&gt;Вступление&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
Для некоторых программистов, привыкших писать текст программы одним файлом (или включать в Си-файл другие Си-файлы директивой #include), вызывает трудность оформление и подключение независимых модулей, которые были бы изолированны от основной программы и легко переносились бы в другие проекты. Здесь я опишу, как это делается.
&lt;/p&gt;

&lt;p&gt;
Итак, создается пара файлов с одинаковыми именами и с расширениями .c и .h (одинаковые имена - необязательное условие, но его нарушение приведет к путанице), например &lt;strong&gt;new_module.c&lt;/strong&gt; и &lt;strong&gt;new_module.h&lt;/strong&gt;. Формат и содержание их описан ниже.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Вступление&quot; [189-1218] --&gt;
&lt;h3&gt;&lt;a name=&quot;содержание_основного_файла_.c&quot; id=&quot;содержание_основного_файла_.c&quot;&gt;Содержание основного файла (.c)&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Секция include: здесь подключается заголовочный файл к модулю&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co2&quot;&gt;#include &amp;quot;new_module.h&amp;quot;      // Включаем файл заголовка для нашего модуля&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Секция определения переменных, используемых в модуле&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;// Глобальные&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; GlobalVar1;
&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; GlobalVar2;
...
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;// Локальные&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; LocalVar1;
&lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; LocalVar2;
...
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Секция прототипов локальных функций&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; local_func1 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; local_func2 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
...
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Секция описания функций (сначала глобальных, потом локальных)&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; global_func1 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    ...;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; global_func1 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    ...;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
...
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; local_func1 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    ...;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; local_func1 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    ...;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
...
&amp;nbsp;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  ENF OF FILE&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;описание_секций&quot; id=&quot;описание_секций&quot;&gt;Описание секций&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Секция include&lt;/strong&gt; - В этой секции подключается заголовочный файл от этого-же модуля. Все остальные заголовочные файлы лучше включать в .h-файле, т.к. они, помимо всего прочео, могут иметь описание типов и констант, которые могут использоваться заголовоыным файлом нашего модуля.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Секция определения переменных&lt;/strong&gt; - Здесь определяются переменные, используемые модулем. Причем для наглядности сперва описываются глобальные переменные (те, область видимости которых будет распространяться на другие модули), а затем - локальные (т.е. те, доступ к которым может осуществляться только внутри данного модуля)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Секция прототипов локальных функций&lt;/strong&gt; - Здесь описываются прототипы всех локальных функций данного модуля, т.е. тех функций, которые используются внутри самого модуля и вызываются только функциями этого же модуля.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Секция описания функций&lt;/strong&gt; - В этой секции уже идет оперативная часть кода, т.е. описание самих функций. Причем для удобства желательно также соблюдать последовательность: сначала глобальные, затем локальные. &lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Содержание основного файла (.c)&quot; [1219-5255] --&gt;
&lt;h3&gt;&lt;a name=&quot;содержание_заголовочного_файла_.h&quot; id=&quot;содержание_заголовочного_файла_.h&quot;&gt;Содержание заголовочного файла (.h)&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#ifndef _NEW_MODULE_H        // Блокируем повторное включение этого модуля&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define _NEW_MODULE_H&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Секция include: здесь подключаются заголовочные файлы используемых модулей&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co2&quot;&gt;#include &amp;lt;math.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#include &amp;lt;stdio.h&amp;gt;&lt;/span&gt;
...
&amp;nbsp;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Секция определения констант&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co2&quot;&gt;#define MY_CONST1            1&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define MY_CONST2            2&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define ...&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Секция определения типов&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;struct&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    ...
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; T_STRUCT;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; ...
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Секция определения глобальных переменных&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; GlobalVar1;
&lt;span class=&quot;kw4&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; GlobalVar2;
&lt;span class=&quot;kw4&quot;&gt;extern&lt;/span&gt; ...
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Секция прототипов глобальных функций&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; global_func1 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; global_func2 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
...
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Секция определения макросов&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co2&quot;&gt;#define MACRO1    ...&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define MACRO2    ...&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define ...&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co2&quot;&gt;#endif                       // Закрывающий #endif к блокировке повторного включения&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  ENF OF FILE&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;описание_секций1&quot; id=&quot;описание_секций1&quot;&gt;Описание секций&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Блокировка повторного включение этого модуля&lt;/strong&gt; - один и тот же h-файл может быть включен в несколько модулей, которые также включают заголовочные файлы друг друга. Таким образом получается, что с точки зрения компилятора файл может быть включен в другой файл два или более раз. Тогда бы получалось, что все типы и константы также описаны более одного раза, что вызовет ошибку компилятора (переопределение констант, переопределение типов и т.п.). Чтобы этого не происходило, весь заголовочный файл заключается в скобки #ifndef…#endif. Если компилятор видит, что константа _NEW_MODULE_H не определена, то он включает весь текст файла, в котором, помимо всего прочего, и определяется константа _NEW_MODULE_H. При повторном включении файла компилятор уже видит, что эта константа включена, и все, что заключено в скобки #ifndef…#endif, будет им проигнорировано. &lt;em&gt;(&lt;strong&gt;Примечание&lt;/strong&gt;: имя константы _NEW_MODULE_H должно быть свое для каждого модуля. Для исключения путаницы и возможного повторения имен рекомендуется в качестве имени этой константы брать имя файла в верхнем регистре с суффиксом _H. Ннапример: в файле my_module.h определяем константу _MY_MODULE_H; в файле keyboard.h определяем константу _KEYBOARD_H и т.д.)&lt;/em&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Секция include&lt;/strong&gt; - здесь подключаются заголовочные файлы модулей, используемых нашим модулем. &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Секция определения констант&lt;/strong&gt; - определяются константы, используемые модулем (или определяющие режим работы модуля). Эти константы будут доступны как самому модулю, так и все модулям, включающим этот файл. Если какую-то константу нужно скрыть (например, она используется только в этом модуле, а есть вероятность, что в каком-нибудь стороннем модуле встретится константа с таким же именем), то ее определение можно перенести в основной файл.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Секция определения типов&lt;/strong&gt; - здесь определяются все специфичные для этого модуля типы данных.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Секция определения глобальных переменных&lt;/strong&gt; - здесь описываются переменные, которые будут доступны из других модулей. Следует обратить внимание на то, что в заголовочном файле переменные описываются с обязательным квалификатором &lt;code&gt;extern&lt;/code&gt;.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Секция прототипов глобальных функций&lt;/strong&gt; - Здесь описываются прототипы функций, которые будут видны другим модулям, чтобы компилятор знал, с какими параметрами их можно вызывать.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Секция определения макросов&lt;/strong&gt; - здесь можно описать какие-то присущие модулю макроопределения.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Содержание заголовочного файла (.h)&quot; [5256-11841] --&gt;
&lt;h3&gt;&lt;a name=&quot;подключение_к_проекту&quot; id=&quot;подключение_к_проекту&quot;&gt;Подключение к проекту&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Чтобы включить созданные файлы в свой проект нужно:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; В интегрированной среде добавить в проект оба файла (new_module.c и new_module.h). Можно ограничиться только си-файлом, но для удобства работы с рабочей областью (workspace) лучше добавлять оба. (Например, в MPLAB добавление файлов делается через меню: Project/Add Files to Project).&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Во всех файлах проекта, в которых предполагается использование функций, типов, переменных, констант или макросов из новых файлов, вставить строчку:&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#include &amp;quot;new_module.h&amp;quot;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Подключение к проекту&quot; [11842-12766] --&gt;
&lt;h3&gt;&lt;a name=&quot;примечания&quot; id=&quot;примечания&quot;&gt;Примечания&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

&lt;strong&gt;Примечание 1&lt;/strong&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Все секции, кроме &amp;quot;include&amp;quot; в си-файле и &amp;quot;блокировки повторного включения&amp;quot; в h-файле, являются необязательными, однако, даже если какой-то секции нет (например, в модуле нет локальных функций), то лучше комментарий, описывающий секцию оставить, чтобы при просмотре файла не возникало вопросов: а где эти локальные функции могут быть описаны? а вдруг они где-то в другом месте? и т.п. А так сразу видно, что есть секция, но она пустая, следовательно, локальных функций нет.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;strong&gt;Примечание 2&lt;/strong&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; порядок секций, последовательность описаний внутри секций не имеет значения, если, конечно, не нарушается порядок, определенный стандартом Си (например, прототип функции должен быть описан в файле раньше, чем первое к ней обращение). Кроме того, если описания будут перемешаны между собой (например, сначала идут переменные, потом прототипы, потом опять переменные и т.п.), то ничего страшного, кроме потери наглядности, не произойдет.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;strong&gt;Примечание 3&lt;/strong&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; При необходимости можно добавлять другие секции (напрмер, иногда нужно описать несколько констант внутри основного си-файла).&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;strong&gt;Примечание 4&lt;/strong&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Обратим внимание, что в си-файле нет секции описания прототипов глобальных функций. Их туда можно было бы добавить, но получится, что они просто будут дублировать уже описанные прототипы в h-файле (тут они должны быть обязательно, иначе остальные модули не будут знать про эти функции). Это несколько неудобно, т.к. при смене спецификации функций исправления в прототипах придется делать в двух местах.  &lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Примечания&quot; [12767-] --&gt;</description>
    </item>
    <item rdf:about="http://www.pic24.ru/doku.php/osa/articles/mpasm_formatting?rev=1303575275">
        <dc:format>text/html</dc:format>
        <dc:date>2011-04-23T20:14:35+03:00</dc:date>
        <title>MPASM: как оформлять программы</title>
        <link>http://www.pic24.ru/doku.php/osa/articles/mpasm_formatting?rev=1303575275</link>
        <description>


&lt;h1&gt;&lt;a name=&quot;mpasmкак_оформлять_программы&quot; id=&quot;mpasmкак_оформлять_программы&quot;&gt;MPASM: как оформлять программы&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;

Статья адресована программистам PIC-контроллеров младшего и среднего семейства, пишущим на языке ассемблера. Предполагается, что читатель знаком с архитектурой, набором инструкций и набором регистров специального назначения данных контроллеров.

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/mpasm_formatting.pdf&quot; class=&quot;media mediafile mf_pdf&quot; title=&quot;osa:articles:mpasm_formatting.pdf&quot;&gt;Статья в pdf-формате&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/voltmetr.rar&quot; class=&quot;media mediafile mf_rar&quot; title=&quot;osa:articles:voltmetr.rar&quot;&gt;Пример программы из статьи с файлами проекта для Proteus&amp;#039;а&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
 
 
Ноябрь, 2010
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;MPASM: как оформлять программы&quot; [1-764] --&gt;
&lt;h1&gt;&lt;a name=&quot;mpasmпростые_решения_для_pic16&quot; id=&quot;mpasmпростые_решения_для_pic16&quot;&gt;MPASM: простые решения для PIC16&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;

Небольшая брошюра, где собраны несколько простых советов для увеличения эффективности кода на ассемблере для PIC16. Материал рассчитан на начинающих программистов.

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/mpasm_tricks.pdf&quot; class=&quot;media mediafile mf_pdf&quot; title=&quot;osa:articles:mpasm_tricks.pdf&quot;&gt;Статья в pdf-формате&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
 
 
Январь, 2011
&lt;/p&gt;

&lt;p&gt;
 
 
 
 
&lt;/p&gt;

&lt;p&gt;
Виктор Тимофеев
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;mailto:&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot; class=&quot;mail JSnocheck&quot; title=&quot;&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot;&gt;osa@pic24.ru&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;MPASM: простые решения для PIC16&quot; [765-] --&gt;</description>
    </item>
    <item rdf:about="http://www.pic24.ru/doku.php/osa/articles/pk2_osa_lights?rev=1295426368">
        <dc:format>text/html</dc:format>
        <dc:date>2011-01-19T11:39:28+03:00</dc:date>
        <title>&quot;Бегущие огни&quot; на ОСРВ</title>
        <link>http://www.pic24.ru/doku.php/osa/articles/pk2_osa_lights?rev=1295426368</link>
        <description>


&lt;h1&gt;&lt;a name=&quot;бегущие_огни_на_осрв&quot; id=&quot;бегущие_огни_на_осрв&quot;&gt;&amp;quot;Бегущие огни&amp;quot; на ОСРВ&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;

Статья рассматривает простой пример разработки программы на PIC-контроллере с использованием ОСРВ OSA и является пособием по применению ОСРВ&lt;sup&gt;&lt;a href=&quot;#fn__1&quot; name=&quot;fnt__1&quot; id=&quot;fnt__1&quot; class=&quot;fn_top&quot;&gt;1)&lt;/a&gt;&lt;/sup&gt; в PIC-контроллерах для начинающих. В качестве аппаратной базы выбраны демо-платы из комплектов PicKit2 на базе контроллеров PIC16F886, PIC16F887 и  PIC16F690:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://pickit2.ru/doku.php/что.такое.pickit2#dv164120.-.pickit.2.starter.kit&quot; class=&quot;urlextern&quot; title=&quot;http://pickit2.ru/doku.php/что.такое.pickit2#dv164120.-.pickit.2.starter.kit&quot;  rel=&quot;nofollow&quot;&gt;DV164120 - PICkit 2 Starter Kit&lt;/a&gt; - Программатор PICkit2 + демонстрационная плата с PIC16F690&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://pickit2.ru/doku.php/что.такое.pickit2#dv164121.-.pickit.2.debug.express&quot; class=&quot;urlextern&quot; title=&quot;http://pickit2.ru/doku.php/что.такое.pickit2#dv164121.-.pickit.2.debug.express&quot;  rel=&quot;nofollow&quot;&gt;DV164121 - PICkit™ 2 Debug Express&lt;/a&gt;- Программатор PICkit2 + демонстрационная плата с контроллером PIC16F887&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Бегущие огни на ОСРВ&quot; [1-1127] --&gt;
&lt;h2&gt;&lt;a name=&quot;введение&quot; id=&quot;введение&quot;&gt;Введение&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Чаще всего контроллеры, используемые в электронных устройствах, выполняют сразу несколько задач: интерфейс с пользователем, обмен данными с другими устройствами, слежение за внешними сигналами, управление какими-то электрическими цепями и т.д. Поэтому при написании программ программисту приходится задумываться не только о том, как выполнить какую-то конкретную задачу, но и о том, как распределить ресурсы контроллера между всеми возлагаемыми на него задачами. В первую очередь речь, конечно же, идет о временных ресурсах и о ресурсах памяти. Т.е. приходится думать о том, чтобы все задачи успевали все сделать вовремя, не мешая остальным задачам, и чтобы на все задачи хватило памяти (и программной и памяти данных). Кроме того, нужно будет продумывать способы синхронизации задач между собой (например, если мы делаем термометр, то температуру нельзя выводить на экран до того, как она будет измерена).
&lt;/p&gt;

&lt;p&gt;
Получается, что большинство программ, написанных для микроконтроллеров, являются многозадачными приложениями. И программисту приходится каждый раз, начиная новую программу, прилагать немало усилий для продумывания механизмов обеспечения многозадачности, оптимальных для данной программы. Вполне резонно возникает вопрос: раз проектирование таких механизмов - это такая частая операция, то нет ли возможности его автоматизировать?  Ответ: такая возможность есть - это использование многозадачной операционной системы, которая уже имеет механизмы распределения ресурсов контроллера между задачами, а также средства синхронизации задач между собой. И все, что программисту понадобится, - это изучить работу с операционной системой, а дальше работать с ней, избавив себя от лишней головной боли, связанной с организацией многозадачности и синхронизации задач, и позволив себе тем самым сконцентрироваться на решении конкретных задач.
&lt;/p&gt;

&lt;p&gt;
Однако следует помнить, что операционная система (ОС), распределяя ресурсы контроллера между задачами, и сама тоже требует под свои нужды кое-какие ресурсы (и процессорное время и память). Поэтому, научившись использовать ОС для создания своих проектов, нужно помнить, что ее применение не всегда оправдано. Иногда это бывает просто неудобно, иногда малоресурсный контроллер не способен вместить в себя и все задачи и ОС, иногда время, отнимаемое операционной системой, не позволяет задачам работать с нужно скоростью. Со временем опыт позволит еще на этапе проектирования принять решение: использовать ОС или нет.
&lt;/p&gt;

&lt;p&gt;
Более подробно об основах операционных систем реального времени можно прочитать здесь:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Тим Уилмсхерст, &amp;quot;Разработка встроенных систем с помощью микроконтроллеров PIC&amp;quot; - в 18-ой главе неплохое описание основ ОСРВ. Книгу можно найти в сети.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://www.pumpkininc.com/content/doc/manual/SalvoUserManual-rus-v3.2.3.pdf&quot; class=&quot;urlextern&quot; title=&quot;http://www.pumpkininc.com/content/doc/manual/SalvoUserManual-rus-v3.2.3.pdf&quot;  rel=&quot;nofollow&quot;&gt;Описание ОСРВ Salvo&lt;/a&gt; - рекомендую прочитать вторую главу;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://www.pic24.ru/lib/exe/fetch.php?media=http%3A%2F%2Fjacos.narod.ru%2Fload%2Fjacos_v1070.zip&quot; class=&quot;media mediafile mf_zip&quot; title=&quot;http://jacos.narod.ru/load/jacos_v1070.zip&quot;&gt;ОСРВ jacOS&lt;/a&gt; - стоит почитать описание от автора в папке &amp;quot;doc&amp;quot;;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://wiki.pic24.ru/doku.php/osa/articles/rtos_usage&quot; class=&quot;urlextern&quot; title=&quot;http://wiki.pic24.ru/doku.php/osa/articles/rtos_usage&quot;  rel=&quot;nofollow&quot;&gt;Программирование контроллеров с использованием ОСРВ OSA&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Здесь мы рассмотрим применение операционной системы реального времени &lt;a href=&quot;http://www.pic24.ru/doku.php/osa/ref/intro&quot; class=&quot;urlextern&quot; title=&quot;http://www.pic24.ru/doku.php/osa/ref/intro&quot;  rel=&quot;nofollow&quot;&gt;OSA&lt;/a&gt; для разработки программы &amp;quot;Бегущие огни&amp;quot;. В качестве аппаратной базы будем использовать демо-плату из комплекта PicKit2 с установленным на ней контроллером PIC16F886, PIC16F887 или PIC16F690.
&lt;/p&gt;

&lt;p&gt;
Итак, в нашем распоряжении 4 светодиода (на демо-плате с контроллером 16F887 - 8 светодиодов), кнопка и резистор с переменным сопротивлением. Зададимся задачей сделать программу, управляющую светодиодами так, чтобы:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; каждый из них в один момент времени имел свою яркость;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; яркость светодиодов циклически (по кругу) менялась, создавая эффект движения (вращения);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; кнопкой менялось направление движения;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; переменным сопротивлением регулировалась скорость движения.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Т.е. у нас получится некая &amp;quot;светодиодная картинка&amp;quot; примерно такого вида:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/pk2_osa_8leds.png?id=osa%3Aarticles%3Apk2_osa_lights&quot; class=&quot;media&quot; title=&quot;osa:articles:pk2_osa_8leds.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/pk2_osa_8leds.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Как видно, яркость светодиодов постепенно убывает. Эту картинку мы и будем вращать так, чтобы самый яркий светодиод все время менял свое положение, а остальные следовали за ним шлейфом.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Введение&quot; [1128-8441] --&gt;
&lt;h2&gt;&lt;a name=&quot;как_регулировать_яркость_светодиода&quot; id=&quot;как_регулировать_яркость_светодиода&quot;&gt;Как регулировать яркость светодиода&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Два слова о том, как регулируется яркость светодиода. Есть два способа: 
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; регулировкой силы тока, протекающей через светодиод;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; регулировкой скважности импульсов постоянного тока.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Какой из этих способов использовать, разработчик решает, исходя из элементной базы и требований к устройству. В нашем случае будет удобнее использовать второй способ, т.к. у нас в распоряжении только цифровое управление. В качестве источников постоянного тока будут выступать токоограничивающие резисторы в цепях светодиодов. Мы будем управлять яркостью светодиодов, подавая на них импульсы достаточно высокой частоты, чтобы глаз не замечал мигания. Обычно частоту в таких случаях выбирают в пределах 50-200 Гц. Т.е. каждый период длится от 5 до 20 мс. Сама яркость регулируется скважностью импульсов, т.е. отношением длительности периода ко времени импульса (активного состояния &amp;quot;1&amp;quot;). Очевидно, что чем скважность меньше (т.е. импульс длиннее), тем светодиод будет гореть ярче:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/pk2_osa_pwm_led_control.png?id=osa%3Aarticles%3Apk2_osa_lights&quot; class=&quot;media&quot; title=&quot;osa:articles:pk2_osa_pwm_led_control.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/pk2_osa_pwm_led_control.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
tи - время импульса;
Т - период.
&lt;/p&gt;

&lt;p&gt;
Каково должно быть разрешение активного состояния, т.е. с каким минимальным шагом может изменяться длительность активного состояния? Понятно, что чем разрешение выше (шаг меньше), тем лучше. И опять же: требования к разрешающей способности управляющего светодиодом сигнала определяются назначением устройства. Если это LED-телевизор, то разрешение как минимум должно быть 8-бит. Если же это гирлянда, или какое-нибудь оформление вывески магазина или витрины (как раз то, для чего можно будет применить нашу программу), то тут хватит 4-5 бит.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Как регулировать яркость светодиода&quot; [8442-11340] --&gt;
&lt;h2&gt;&lt;a name=&quot;проектирование&quot; id=&quot;проектирование&quot;&gt;Проектирование&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

В данном примере мы рассмотрим самый простой вариант применения ОСРВ: все задачи будут иметь одинаковый приоритет, обмен данными между задачами будет производиться через глобальные переменные. Т.е. сейчас будем использовать ОСРВ только для обеспечения параллельного выполнения нескольких подпрограмм (задач). 
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Проектирование&quot; [11341-11956] --&gt;
&lt;h3&gt;&lt;a name=&quot;задачи&quot; id=&quot;задачи&quot;&gt;Задачи&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Для начала определимся, какие задачи будет выполнять наш контроллер:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; формирование 4-х (8-ми для 16F887) ШИМ&lt;sup&gt;&lt;a href=&quot;#fn__2&quot; name=&quot;fnt__2&quot; id=&quot;fnt__2&quot; class=&quot;fn_top&quot;&gt;2)&lt;/a&gt;&lt;/sup&gt; каналов для управления каждым светодиодом; частотой в пределах 50-200 Гц и разрешением 5 бит;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &amp;quot;вращение&amp;quot; - подпрограмма, которая будет вращать &amp;quot;светодиодную картинку&amp;quot; с заданной скоростью;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; опрос кнопки;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; опрос позиции переменного резистора; другими словами: измерение напряжения на входе АЦП.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Учитывая то, что задача формирования ШИМ-сигналов требовательна к скорости, ее есть смысл поместить в обработчик прерывания. Остальные задачи будут в виде простых Си-функций с некоторыми особенностями, о которых поговорим позже.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Задачи&quot; [11957-13199] --&gt;
&lt;h3&gt;&lt;a name=&quot;данные&quot; id=&quot;данные&quot;&gt;Данные&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Теперь подумаем о том, какими данными будет оперировать программа и какими данными будут обмениваться задачи. 
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; очевидно, что нам нужны какие-то константы, определяющие длительности импульсов для разных яркостей (дело в том, что зависимость яркости светодиода от длительности импульсов нелинейная). Т.к. у нас 4 светодиода (или 8) то нам нужен массив из 4-х (8-ми) констант;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; т.к. &amp;quot;светодиодная картинка&amp;quot; вращается, то нам нужна глобальная переменная, показывающая стадию вращения на данный момент;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; учитывая, что вращение может происходить то в одну, то в другую сторону, нам нужна переменная, показывающая направление вращения;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; наконец, нужна переменная, определяющая скорость вращения.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Итак, блок определения глобальных переменных (и констант) в нашей программе будет выглядеть так:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; Brightness&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;31&lt;/span&gt;, &lt;span class=&quot;nu0&quot;&gt;11&lt;/span&gt;, &lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;, &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;; &lt;span class=&quot;co1&quot;&gt;// Таблица яркостей&lt;/span&gt;
                                          &lt;span class=&quot;co1&quot;&gt;// (длительностей импульсов)&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; m_cPosition;       &lt;span class=&quot;co1&quot;&gt;// Текущая фаза вращения (позиция яркости в&lt;/span&gt;
                        &lt;span class=&quot;co1&quot;&gt;// таблице Brightness для первого светодиода)&lt;/span&gt;
&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; m_cDirection;      &lt;span class=&quot;co1&quot;&gt;// Направление вращения (-1 или +1)&lt;/span&gt;
&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; m_cSpeed;          &lt;span class=&quot;co1&quot;&gt;// Скорость вращения&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;strong&gt;Примечание&lt;/strong&gt;:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; префикс &amp;quot;m_&amp;quot; в именах переменных говорит о том, что эти переменные глобальные;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; префикс &amp;quot;c&amp;quot; говорит о том, что переменные имеют тип &lt;strong&gt;char&lt;/strong&gt;.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Данные&quot; [13200-15480] --&gt;
&lt;h2&gt;&lt;a name=&quot;реализация&quot; id=&quot;реализация&quot;&gt;Реализация&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Теперь, когда мы определились с количеством и назначением задач, приступим к их программной реализации. Для начала определимся с системными параметрами:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; воспользуемся внутренним тактовым генератором контроллера (8 МГц);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; все обрабатываемые светодиоды подключены к выводам одного порта последовательно. Эта особенность позволит работать с ними в цикле;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; кнопка имеет активное состояние &amp;quot;0&amp;quot;;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; напряжение на входе АЦП изменяется в пределах 0..VDD, т.е. для выбора скорости мы можем использовать весь диапазон значений 0..255.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

&lt;strong&gt;&lt;em&gt;Примечания.&lt;/em&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;em&gt;Т.к. это наш первый проект с использованием ОСРВ, то мы постараемся обойтись минимальным набором системных сервисов;&lt;/em&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;em&gt;Для лучшей читабельности и упрощения алгоритма работы программы некоторые фрагменты будут написаны несколько развернуто, в ущерб оптимальности.&lt;/em&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Реализация&quot; [15481-17004] --&gt;
&lt;h3&gt;&lt;a name=&quot;шим&quot; id=&quot;шим&quot;&gt;ШИМ&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Итак, нам нужно сформировать 4 (или 8) ШИМ-каналов с частотой 50-200 Гц и разрешением 5 бит. Для этой цели воспользуемся прерыванием по TMR0. 
&lt;/p&gt;

&lt;p&gt;
Предлагаю это сделать так:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/pk2_osa_pwm.png?id=osa%3Aarticles%3Apk2_osa_lights&quot; class=&quot;media&quot; title=&quot;osa:articles:pk2_osa_pwm.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/pk2_osa_pwm.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
На рисунке видно, что один период ШИМ будет проходить за 32 периода TMR0 (на картинке обозначен как t0). Т.е. мы должны завести внутреннюю переменную-счетчик, которая будет увеличиваться при каждом такте ШИМ (каждом прерывании от TMR0) и сравниваться со значениями из таблицы &lt;strong&gt;Brightness&lt;/strong&gt;, которую мы описали выше. Пока переменная меньше соответствующего каналу значения таблицы, светодиод горит.
&lt;/p&gt;

&lt;p&gt;
Учитывая тактовую частоту, удобно выбирать значение одного шага ШИМ кратное одному периоду TMR0, т.е. 128 мкс. Но нужно помнить, что обработка 4-х (или 8-ми) каналов ШИМ требует времени, поэтому мы для начала возьмем период побольше, с запасом. Установим прескейлер для таймера 0 равным 4 и получим длительность шага ШИМ ~0.5мс (512 мкс). Т.е. период ШИМ будет равен 32*0.5мс = 16мс, т.е. частота = 62 Гц.
&lt;/p&gt;

&lt;p&gt;
Не будем забывать также, что у нас есть переменные &lt;strong&gt;m_cPosition&lt;/strong&gt;, показывающая текущую фазу вращения (позицию значения яркости из массива &lt;strong&gt;Brightness&lt;/strong&gt; для первого светодиода), и m_cDirection, показывающя направление вращения. Их нужно будет учесть при написании обработчика.
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; interrupt isr &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;// Нам понадобятся локальные переменные для обработки ШИМ&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; cCounter;   &lt;span class=&quot;co1&quot;&gt;// Счетчик шагов ШИМ.&lt;/span&gt;
           &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; cPosition;  &lt;span class=&quot;co1&quot;&gt;// Переменная для обеспечения вращения&lt;/span&gt;
           &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; cLedsMask;  &lt;span class=&quot;co1&quot;&gt;// Маска текущего светодиода&lt;/span&gt;
           &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; i;          &lt;span class=&quot;co1&quot;&gt;// Переменная для организации цикла по&lt;/span&gt;
                            &lt;span class=&quot;co1&quot;&gt;// всем ШИМ-каналам&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;T0IF &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; T0IE&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        T0IF &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
        cCounter++;
&amp;nbsp;
        cPosition &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; m_cPosition;    &lt;span class=&quot;co1&quot;&gt;// Позиция яркости для первого&lt;/span&gt;
                                    &lt;span class=&quot;co1&quot;&gt;// светодиода&lt;/span&gt;
&amp;nbsp;
        i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;;                      &lt;span class=&quot;co1&quot;&gt;// Цикл по всем светодиодам&lt;/span&gt;
        cLedsMask &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x01&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
            &lt;span class=&quot;co1&quot;&gt;//-----------------------------------------------------&lt;/span&gt;
            &lt;span class=&quot;co1&quot;&gt;// Проверяем, не пора ли гасить светодиод. Для этого&lt;/span&gt;
            &lt;span class=&quot;co1&quot;&gt;// сравниваем текущий шаг ШИМ со значением яркости для&lt;/span&gt;
            &lt;span class=&quot;co1&quot;&gt;// текущего светодиода&lt;/span&gt;
            &lt;span class=&quot;co1&quot;&gt;//-----------------------------------------------------&lt;/span&gt;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;cCounter &lt;span class=&quot;sy1&quot;&gt;&amp;gt;&lt;/span&gt; Brightness&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;cPosition &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
                   PORTLEDS &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; ~cLedsMask;
            &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;
                   PORTLEDS |=  cLedsMask;
&amp;nbsp;
            cLedsMask &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;          &lt;span class=&quot;co1&quot;&gt;// Берем следующий светодиод&lt;/span&gt;
            cPosition &lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; m_cDirection;&lt;span class=&quot;co1&quot;&gt;// В зависимости от направления&lt;/span&gt;
                                      &lt;span class=&quot;co1&quot;&gt;// вращения берем позицию яркости&lt;/span&gt;
                                      &lt;span class=&quot;co1&quot;&gt;// для следующего светодиода&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;--&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
        cCounter &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x1F&lt;/span&gt;;             &lt;span class=&quot;co1&quot;&gt;// ШИМ 5-разрядный, старшие&lt;/span&gt;
                                      &lt;span class=&quot;co1&quot;&gt;// разряды обнуляются&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;   &lt;span class=&quot;co1&quot;&gt;// T0IF&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;strong&gt;Примечание&lt;/strong&gt;: &lt;em&gt;учитывая специфику подпрограммы-прерывания, компилятор все переменные, объявленные в теле прерывания по умолчанию делает &lt;strong&gt;static&lt;/strong&gt;. Тем не менее, сохраняя смысл эти переменных, мы нарочно поставили квалификатор &lt;strong&gt;static&lt;/strong&gt; только перед &lt;strong&gt;cCounter&lt;/strong&gt;, давая самим себе понять, что нам важно сохранение значения этой переменной после выхода из прерывания. Остальные переменные с точки зрения алгоритма - временные. Такая детализация может оказаться полезной, если код обработки ШИМ впоследствии будет вынесен из прерывания в обычную функцию.&lt;/em&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;ШИМ&quot; [17005-22322] --&gt;
&lt;h3&gt;&lt;a name=&quot;чтение_данных_с_ацпвыбор_скорости_вращения&quot; id=&quot;чтение_данных_с_ацпвыбор_скорости_вращения&quot;&gt;Чтение данных с АЦП: выбор скорости вращения&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Эту задачу мы оформим в виде задачи операционной системы. Как уже говорилось выше, задачи в ОС - это обычные Си-функции с некоторыми особенностями, а именно:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; тело функции должно содержать бесконечный цикл;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; из этих функций нельзя выходить по &lt;strong&gt;return&lt;/strong&gt;, можно только используя специальные сервисы ОС, переключающие контекст. Внутри каждой задачи обязательно должен быть вызов хотя бы одного такого сервиса;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; функции не вызываются напрямую в теле программы, их вызывает планировщик ОС&lt;sup&gt;&lt;a href=&quot;#fn__3&quot; name=&quot;fnt__3&quot; id=&quot;fnt__3&quot; class=&quot;fn_top&quot;&gt;3)&lt;/a&gt;&lt;/sup&gt;.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

В нашей программе есть переменная &lt;strong&gt;m_cSpeed&lt;/strong&gt;, которая определяет скорость вращения картинки. Эту переменную мы и будем формировать в данной задаче. Очевидно, что частота обновления этой переменной должна быть пропорциональна скорости изменения напряжения на входе АЦП. Медленнее делать нельзя, т.к. пользователю нужно сразу видеть, как меняется скорость при повороте потенциометра. Быстрее - нет смысла, т.к. на глаз разница не будет заметна, а система окажется перегружена слишком частыми запусками этой задачи. Поэтому мы выберем интервал обновления, равный 100 мс. Т.е. сделаем так, чтобы задача запускалась раз в 100 мс, измеряла напряжение на входе АЦП и формировала новое значение переменной &lt;strong&gt;m_cSpeed&lt;/strong&gt;. Все остальное время задача будет находиться в ожидании и не будет мешать выполняться остальным задачам.
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task_SetSpeed &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        CHS0 &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;          &lt;span class=&quot;co1&quot;&gt;// Выбираем нулевой канал АЦП. В принципе,&lt;/span&gt;
        CHS1 &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;          &lt;span class=&quot;co1&quot;&gt;// не очень нужный код, но если впоследствии&lt;/span&gt;
        CHS2 &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;          &lt;span class=&quot;co1&quot;&gt;// в программу будет добавлена обработка АЦП&lt;/span&gt;
        CHS3 &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;          &lt;span class=&quot;co1&quot;&gt;// по другим каналам, то этот код окажется&lt;/span&gt;
                           &lt;span class=&quot;co1&quot;&gt;// очень полезным&lt;/span&gt;
&amp;nbsp;
        GODONE &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;            &lt;span class=&quot;co1&quot;&gt;// Запуск измерения&lt;/span&gt;
&amp;nbsp;
        OS_Cond_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;GODONE&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;; &lt;span class=&quot;co1&quot;&gt;// Ожидание завершения измерения.&lt;/span&gt;
                               &lt;span class=&quot;co1&quot;&gt;// Во время ожидания могут выполняться&lt;/span&gt;
                               &lt;span class=&quot;co1&quot;&gt;// другие задачи&lt;/span&gt;
&amp;nbsp;
        m_cSpeed &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; ADRESH;     &lt;span class=&quot;co1&quot;&gt;// Установка нового значения скорости&lt;/span&gt;
&amp;nbsp;
        OS_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt; ms&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;      &lt;span class=&quot;co1&quot;&gt;// Говорим ОС, что следующий раз эту&lt;/span&gt;
                               &lt;span class=&quot;co1&quot;&gt;// задачунадо запустить через 100 мс.&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;strong&gt;Примечание&lt;/strong&gt;:&lt;em&gt; ms - макрос, определенный как &amp;quot;/ 1&amp;quot;. Подробнее о нем - в параграфе &amp;quot;Таймер&amp;quot;.&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
Здесь мы использовали два системных сервиса: &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/ref/allservices/OS_Cond_Wait&quot; class=&quot;urlextern&quot; title=&quot;http://www.pic24.ru/doku.php/osa/ref/allservices/OS_Cond_Wait&quot;  rel=&quot;nofollow&quot;&gt;OS_Cond_Wait&lt;/a&gt;&lt;/strong&gt; и &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/ref/allservices/OS_Delay&quot; class=&quot;urlextern&quot; title=&quot;http://www.pic24.ru/doku.php/osa/ref/allservices/OS_Delay&quot;  rel=&quot;nofollow&quot;&gt;OS_Delay&lt;/a&gt;&lt;/strong&gt;. Остановимся на них и рассмотрим, как они работают. 
&lt;/p&gt;

&lt;p&gt;
Сервис &lt;strong&gt;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Cond_Wait&lt;/strong&gt; - это сервис, который переводит задачу в &lt;strong&gt;режим ожидания&lt;/strong&gt; до тех пор, пока не будет выполнено условие в скобках. Что такое режим ожидания? Как уже было сказано ОС имеет встроенный механизм обеспечения многозадачности, что позволяет всем задачам выполняться параллельно (т.е., конечно, они выполняются последовательно, но быстро сменяют друг друга, что создает эффект параллельности). Если какая-то задача ждет выполнения какого-то условия, то ее нет смысла запускать, пока условие не выполнено. Это позволит системе больше внимания уделять остальным задачам. Сервис &lt;strong&gt;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Cond_Wait&lt;/strong&gt; сообщает системе, что эту задачу не нужно запускать, пока условие &lt;strong&gt;!GODONE&lt;/strong&gt; не будет выполнено. Одновременно с этим этот сервис осуществляет выход из функции-задачи (с запоминанием точки выхода), после чего управление передается планировщику ОС. Когда планировщик обнаружит, что бит &lt;strong&gt;GODONE&lt;/strong&gt; сброшен, он переводит задачу в &lt;strong&gt;режим готовности&lt;/strong&gt; - задача становится в очередь выполняемых задач и получит управление, когда до нее дойдет очередь, причем она продолжит свое выполнение с того места, откуда был совершен выход, т.е. сразу же за сервисом &lt;strong&gt;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Cond_Wait&lt;/strong&gt;.
&lt;/p&gt;

&lt;p&gt;
Сервис &lt;strong&gt;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay&lt;/strong&gt; - это сервис, который переводит задачу в режим ожидания на указанное время. Управление передается планировщику. В течение указанного времени задача не будет получать управление, позволяя системе больше времени уделять остальным задачам. Время задается в так называемых &lt;strong&gt;системных тиках&lt;/strong&gt; - периодах вызова системного сервиса &lt;strong&gt;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer&lt;/strong&gt;. Подробнее см. параграф &amp;quot;Таймер&amp;quot;.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Чтение данных с АЦП: выбор скорости вращения&quot; [22323-29510] --&gt;
&lt;h3&gt;&lt;a name=&quot;вращение_с_заданной_скоростью&quot; id=&quot;вращение_с_заданной_скоростью&quot;&gt;Вращение с заданной скоростью&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Эта задача должна выдерживать паузу в соответствии с установленным значением переменной &lt;strong&gt;m_cSpeed&lt;/strong&gt;, после чего увеличивает значение переменной &lt;strong&gt;m_cPosition&lt;/strong&gt; (позиция элемента яркости в массиве Brightness для первого светодиода). Напрашивается красивое использование сервиса &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    OS_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;m_cSpeed&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Однако здесь есть подвох. Если на момент вызова сервиса переменная m_cSpeed имеет большое значение, то пока идет длинная задержка, программа не будет реагировать на изменение скорости. А это будет немного раздражать. Поэтому правильнее организовать задачу так:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task_Rolling &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; cDelay;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        cDelay &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;cDelay&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; m_cSpeed&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;      &lt;span class=&quot;co1&quot;&gt;// Ждем нужное время&lt;/span&gt;
            OS_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
        m_cPosition ++;                  &lt;span class=&quot;co1&quot;&gt;// Изменяем позицию яркости&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Такой подход обеспечит более быструю реакцию на изменение переменной &lt;strong&gt;m_cSpeed&lt;/strong&gt;. Однако и он не лишен недостатков: при больших значениях &lt;strong&gt;m_cSpeed&lt;/strong&gt; задача будет часто получать управление впустую (каждый системный тик). Но с точки зрения интерфейса пользователя такой подход более удачный.
&lt;/p&gt;

&lt;p&gt;
Обратим внимание на объявление переменной &lt;strong&gt;cDelay&lt;/strong&gt;. Эта переменная объявлена как &lt;strong&gt;static&lt;/strong&gt;, т.к. нам важно сохранение ее значения после выхода из функции. Если мы не напишем &lt;strong&gt;static&lt;/strong&gt;, то после выхода из функции эта переменная может затереться локальными переменными других функций.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Вращение с заданной скоростью&quot; [29511-31921] --&gt;
&lt;h3&gt;&lt;a name=&quot;опрос_кнопкивыбор_направления_вращения&quot; id=&quot;опрос_кнопкивыбор_направления_вращения&quot;&gt;Опрос кнопки: выбор направления вращения&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Еще одна простая задача, функцией которой является изменение направления вращение при нажатии кнопки. Т.к. кнопка имеет активный уровень &amp;quot;0&amp;quot;, то сперва мы ждем установки входа на ножке pin_BUTTON в &amp;quot;0&amp;quot;. После того, как дождались нам нужно подавить дребезг, чтобы исключить ложные срабатывания. Для этого мы будем выжидать 40 мс и делать повторную проверку. Если состояние входа так и останется в &amp;quot;0&amp;quot;, значит, кнопка нажата и можно продолжать работать. Если нет, то делаем повторное ожидание. После обработки кнопки мы таким же способом ждем отпускание кнопки.
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task_Button &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  Ожидаем нажатие кнопки&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
            OS_Cond_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;pin_BUTTON&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
            OS_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;40&lt;/span&gt; ms&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;      &lt;span class=&quot;co1&quot;&gt;// Для подавления дребезга контактов&lt;/span&gt;
                                  &lt;span class=&quot;co1&quot;&gt;// ждем 40 мс и делаем повторную&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;pin_BUTTON&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;     &lt;span class=&quot;co1&quot;&gt;// проверку&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  Меняем направление вращения на противоположное&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//&lt;/span&gt;
        m_cDirection &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt;m_cDirection;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  Ожидаем отпускание кнопки&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
            OS_Cond_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;pin_BUTTON&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
            OS_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;40&lt;/span&gt; ms&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;      &lt;span class=&quot;co1&quot;&gt;// Для подавления дребезга контактов&lt;/span&gt;
                                  &lt;span class=&quot;co1&quot;&gt;// ждем 40 мс и делаем повторную&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;pin_BUTTON&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;    &lt;span class=&quot;co1&quot;&gt;// проверку&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Обратим внимание на особенность этой задачи. Если кнопка не будет нажата, то задача никогда не получит управление и не будет загружать процессор, в отличие от двух других задач, которые периодически управление будет получать.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Опрос кнопки: выбор направления вращения&quot; [31922-34804] --&gt;
&lt;h3&gt;&lt;a name=&quot;функция_main&quot; id=&quot;функция_main&quot;&gt;Функция main()&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Итак, мы написали все подпрограммы для работы нашего алгоритма. Пока что все они - обычные функции в стиле языка Си. Операционная система еще не знает, какие из этих функций следует рассматривать как задачи ОС, а какие - сами по себе. Для того чтобы она знала, какими функциями ей предстоит оперировать (т.е. какие функции должны выполняться параллельно), ей нужно сообщить их названия. Для этого есть сервис &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/ref/allservices/OS_Task_Create&quot; class=&quot;urlextern&quot; title=&quot;http://www.pic24.ru/doku.php/osa/ref/allservices/OS_Task_Create&quot;  rel=&quot;nofollow&quot;&gt;OS_Task_Create&lt;/a&gt;&lt;/strong&gt;, которому в параметрах передается имя функции-задачи и ее приоритет. Т.к. изначально мы условились, что все задачи будут равноприоритетными, то всем задачам присвоим высший (нулевой) приоритет.
&lt;/p&gt;

&lt;p&gt;
У нас 4 задачи, которые должны будут выполняться параллельно. Но учитывая, что код одной из задач расположен в прерывании, т.е. она и так уже сама по себе будет выполняться в фоновом режиме, то сервисом &lt;strong&gt;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Task_Create&lt;/strong&gt; нужно создать только три задачи.
&lt;/p&gt;

&lt;p&gt;
Теперь отсталость только добавить инициализацию контроллера и системы. И все: можно запускать планировщик в работу.
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; main &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; prs;
&amp;nbsp;
    Init&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                &lt;span class=&quot;co1&quot;&gt;// Инициализация периферии&lt;/span&gt;
&amp;nbsp;
    OS_Init&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;             &lt;span class=&quot;co1&quot;&gt;// Инициализация операционной системы&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;co1&quot;&gt;//----------------------------------------------------------------&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;// Создаем функции-задачи, которые будут работать&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;// параллельно. В нашем случае все имеют одинаковый&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;// высший приоритет (0)&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//----------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
    OS_Task_Create&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;, Task_Rolling&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Task_Create&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;, Task_SetSpeed&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Task_Create&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;, Task_Button&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
                           &lt;span class=&quot;co1&quot;&gt;// Начальные значения:&lt;/span&gt;
    m_cPosition  &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;      &lt;span class=&quot;co1&quot;&gt;// для фазы вращения&lt;/span&gt;
    m_cDirection &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;      &lt;span class=&quot;co1&quot;&gt;// для направления вращения&lt;/span&gt;
&amp;nbsp;
    OS_EI&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;               &lt;span class=&quot;co1&quot;&gt;// Разрешаем прерывания&lt;/span&gt;
&amp;nbsp;
    OS_Run&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;              &lt;span class=&quot;co1&quot;&gt;// Запускаем планировщик в работу.&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Помимо &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Task_Create, мы тут встречаем еще три сервиса ОС: &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/ref/allservices/OS_Init&quot; class=&quot;urlextern&quot; title=&quot;http://www.pic24.ru/doku.php/osa/ref/allservices/OS_Init&quot;  rel=&quot;nofollow&quot;&gt;OS_Init&lt;/a&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/ref/allservices/OS_EI&quot; class=&quot;urlextern&quot; title=&quot;http://www.pic24.ru/doku.php/osa/ref/allservices/OS_EI&quot;  rel=&quot;nofollow&quot;&gt;OS_EI&lt;/a&gt;&lt;/strong&gt; и &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/ref/allservices/OS_Run&quot; class=&quot;urlextern&quot; title=&quot;http://www.pic24.ru/doku.php/osa/ref/allservices/OS_Run&quot;  rel=&quot;nofollow&quot;&gt;OS_Run&lt;/a&gt;&lt;/strong&gt;.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Init&lt;/strong&gt; - начальная инициализация операционной системы. Здесь обнуляются все системные переменные, подготавливаются дескрипторы задач. Этот сервис должен вызываться первым из всех системных сервисов.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_EI&lt;/strong&gt; - разрешение прерываний. Можно было бы просто воспользоваться строчкой &amp;quot;GIE = 1;&amp;quot;, но тогда, если мы когда-нибудь захотим перевести программу на PIC 18-ой серии, то нам придется вспомнить, что там два уровня прерываний и что, возможно, нужно добавить еще и разрешение GIEL. Этот же сервис все делает автоматически.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Run&lt;/strong&gt; - этот сервис должен вызываться в самом конце функции main(). Т.к. этот сервис является макросом, содержащем внутри себя бесконечный цикл, то все, что будет написано после него никогда не получит управление. Внутри этого сервиса происходит перебор все задач ОС, проверка их готовности или условий выхода из режима ожидания, сравнение приоритетов, выбор самой приоритетной из готовых задач и передача ей управления.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Функция main()&quot; [34805-39777] --&gt;
&lt;h3&gt;&lt;a name=&quot;функция_init&quot; id=&quot;функция_init&quot;&gt;Функция Init()&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Весь текст я здесь приводить не буду (его можно посмотреть в исходных текстах, прилагаемых к статье), т.к. из-за того, что эта функция предусматривает работу на 4-х разных контроллерах (16F886, 16F887, 16F690 и, &amp;quot;по совету друзей&amp;quot;, 16F88), то код ее довольно громоздкий из-за наличия условных директив #ifdef…#endif.
&lt;/p&gt;

&lt;p&gt;
Скажу только, что в этой функции производится инициализация:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; потов ввода/вывода;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; АЦП;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; таймеров;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; прерываний.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Функция Init()&quot; [39778-40557] --&gt;
&lt;h3&gt;&lt;a name=&quot;таймер&quot; id=&quot;таймер&quot;&gt;Таймер&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Последнее, что нам осталось сделать, - это добавить обработку системного таймера. Т.к. у нас в программе вызываются системные сервисы, использующие системный таймер, то нам нужно в периодическое место в программе добавить вызов сервиса &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/ref/allservices/OS_Timer&quot; class=&quot;urlextern&quot; title=&quot;http://www.pic24.ru/doku.php/osa/ref/allservices/OS_Timer&quot;  rel=&quot;nofollow&quot;&gt;OS_Timer&lt;/a&gt;&lt;/strong&gt;. Этот сервис будет следить за всеми задержками, выполняющимися через &lt;strong&gt;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay&lt;/strong&gt;. Теперь нам нужно организовать периодическое место в программе, т.е. то место, куда программа будет попадать с заданным интервалом. У нас, в принципе, такое место уже есть - это прерывание по TMR0. Но мы для сохранения наглядности не будем его трогать, а организуем прерывание по таймеру TMR2. Кроме наглядности, здесь есть еще одно преимущество: мы можем отдельно изменять скорость работы ШИМ и интервал вызовов &lt;strong&gt;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer&lt;/strong&gt;, если понадобится.
&lt;/p&gt;

&lt;p&gt;
В подпрограмму обработки прерываний добавляем такой код:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;TMR2IF&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        TMR2IF &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
        OS_Timer&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Теперь все значения, передаваемые сервису &lt;strong&gt;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay&lt;/strong&gt; в задачах, будут измеряться именно в интервалах вызова сервиса &lt;strong&gt;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer&lt;/strong&gt; - системных тиках.
&lt;/p&gt;

&lt;p&gt;
Я не случайно упомянул возможность появления необходимости изменения интервала вызова системного таймера. Причины могут быть разные, ну, например, мы используем контроллер, имеющий на борту всего один таймер, и нам, хочешь - не хочешь, придется использовать один таймер и для генерации ШИМ-сигналов и для системного таймера. А тут может понадобиться некоторая настройка периода ШИМ. Получается, что каждый раз, меняя период прерывания (и, следовательно, - период вызова &lt;strong&gt;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer&lt;/strong&gt;), нужно будет пересчитывать все константы в параметрах вызовов сервиса &lt;strong&gt;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay&lt;/strong&gt;? Вот тут нам на помощь придет константа &lt;strong&gt;ms&lt;/strong&gt;, о которой речь шла выше. В нашем случае эта константа определена так:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define ms    / 1&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
, т.к. период таймера 2 у нас равен 1 мс. Во всех вызовах сервиса &lt;strong&gt;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay&lt;/strong&gt; мы пользуемся этой константой:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    OS_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt; ms&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;   &lt;span class=&quot;co1&quot;&gt;// Компилятор сделает подстановку &amp;quot;100 / 1&amp;quot;&lt;/span&gt;
    ...
    &lt;span class=&quot;me1&quot;&gt;OS_Delay&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;20&lt;/span&gt; ms&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;    &lt;span class=&quot;co1&quot;&gt;// Компилятор сделает подстановку &amp;quot;20 / 1&amp;quot;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
И если нам придется изменить период вызова &lt;strong&gt;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer&lt;/strong&gt; (повторюсь: не важно, по какой причине), то нам не придется пересчитывать все константы в программе, а достаточно будет только изменить константу ms. Например, мы увеличили период втрое, тогда надо будет переопределить константу так:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define ms    / 3&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
или мы уменьшили период вдвое:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define ms    * 2&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Таймер&quot; [40558-44773] --&gt;
&lt;h3&gt;&lt;a name=&quot;конфигурация_osa&quot; id=&quot;конфигурация_osa&quot;&gt;Конфигурация OSA&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

OSA - очень гибкая в настройке операционная система. Благодаря своей гибкости, она позволяет использовать ресурсы контроллера с максимальной эффективностью для конкретного проекта. Она позволяет программисту выбирать:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; типы используемых системных переменных (таймеров, семафоров, сообщений);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; банки RAM для хранения каждого типа системных переменных;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; какие сервисы и как будут использоваться системой;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; будут ли сервисы использованы в прерываниях;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; и т.д.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Все эти настройки программист указывает в файле OSAcfg.h, который должен быть расположен в папке проекта. Для каждого проекта - свой файл. Подробнее обо всех настройках можно почитать в описании OSA в параграфе &lt;a href=&quot;http://wiki.pic24.ru/doku.php/osa/ref/appendix/configuration&quot; class=&quot;urlextern&quot; title=&quot;http://wiki.pic24.ru/doku.php/osa/ref/appendix/configuration&quot;  rel=&quot;nofollow&quot;&gt;Конфигурация OSAcfg.H&lt;/a&gt;. Создавать и сопровождать этот файл вручную довольно сложно из-за большого количества различных определений, и это могут делать только имеющие опыт работы с OSA программисты. Удобнее для конфигурирования воспользоваться утилитой из комплекта поставки &lt;a href=&quot;http://www.pic24.ru/lib/exe/fetch.php?media=http%3A%2F%2Fwiki.pic24.ru%2Flib%2Fexe%2Ffetch.php%2Fosa%2Fosacfg_tool.rar&quot; class=&quot;media mediafile mf_rar&quot; title=&quot;http://wiki.pic24.ru/lib/exe/fetch.php/osa/osacfg_tool.rar&quot;&gt;OSAcfg_Tool&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
Запустив эту утилиту, мы увидим на экране все настройки, которые можно сделать при конфигурировании проекта. Итак, займемся созданием файла конфигурации для нашего проекта.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;выбираем_папку_где_располагается_наш_проект&quot; id=&quot;выбираем_папку_где_располагается_наш_проект&quot;&gt;1. Выбираем папку, где располагается наш проект&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Для этого в самом верху окна справа нажимаем кнопку &lt;strong&gt;Browse&lt;/strong&gt;. Там выбираем путь к файлу OSAcfg.h - путь к нашему проекту (&amp;quot;C:\TEST\PICKIT2\LIGHTS&amp;quot;). Нажимаем OK. Если файл еще не создан, то программа спросит у Вас, действительно ли Вы хотите создать этот файл. Смело отвечаем &amp;quot;Yes&amp;quot; и идем дальше. (Если файл уже существует, то он просто загрузится и установит в рабочем окне все галочки и параметры в соответствие со своей конфигурацией. Пока же мы считаем, что файл не создан.) 
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;выбираем_имя_проекта&quot; id=&quot;выбираем_имя_проекта&quot;&gt;2. Выбираем имя проекта&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

В поле &lt;strong&gt;Name&lt;/strong&gt; можно ввести имя проекта. Этот пункт необязателен, а имя вводится исключительно для наглядности, чтобы не путаться потом, какой файл от какого проекта. Мы введем в эту строку &amp;quot;Бегущие огни&amp;quot;.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;выбираем_платформу&quot; id=&quot;выбираем_платформу&quot;&gt;3. Выбираем платформу&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Также необязательный пункт. Служит только для того, чтобы пользователь при конфигурировании файла в реальном времени наблюдал предполагаемый расход оперативной памяти операционной системой. Для успокоения выберем платформу: 14-бит (PIC12, PIC16)(ht-picc). Теперь при изменении настроек мы автоматически в рамке &lt;strong&gt;RAM statistic&lt;/strong&gt; будем видеть, сколько байтов в каком банке памяти израсходовано.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;конфигурируем_наш_проект&quot; id=&quot;конфигурируем_наш_проект&quot;&gt;4. Конфигурируем наш проект&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Для этого есть 4 области в рабочем окне: 
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;System&lt;/strong&gt; - системные настройки&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Data services&lt;/strong&gt; - настройки сервисов обмена данными (счетные семафоры, сообщения, очереди сообщений)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Timers&lt;/strong&gt; - все настройки относящиеся к таймерам&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Binary semaphores&lt;/strong&gt; - настройка двоичных семафоров&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Нам понадобятся только &lt;strong&gt;System&lt;/strong&gt; и &lt;strong&gt;Timers&lt;/strong&gt;, т.к. сервисы обмена данными (включая бинарные семафоры) мы не используем.
&lt;/p&gt;

&lt;p&gt;
Учитывая, что мы решили не использовать приоритеты (т.е. все задачи сделать равноприоритетными), можно установить галочку напротив пункта &lt;strong&gt;Disable priority&lt;/strong&gt;. Это сократит размер кода ядра операционной системы и ускорит работу планировщика.
&lt;/p&gt;

&lt;p&gt;
Далее, нам обязательно нужно выбрать количество задач ОС, которые будут работать одновременно. В нашем случае - 3 (по количеству задач, создаваемых сервисом &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Task_Create; как уже было сказано раньше, 4-я задача у нас не является задачей ОС и располагается в обработчике прерывания).
&lt;/p&gt;

&lt;p&gt;
Последнее, что нам понадобится, - это включить таймер задач, т.е. поставить галочку напротив пункта &lt;strong&gt;Task timers&lt;/strong&gt;.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;сохраняем_и_выходим&quot; id=&quot;сохраняем_и_выходим&quot;&gt;5. Сохраняем и выходим&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Жмем на кнопку &lt;strong&gt;Save&lt;/strong&gt;, чтобы сохранить отредактированный файл конфигурации, и выходим из программы нажатием на кнопку &lt;strong&gt;Exit&lt;/strong&gt;. Теперь, заглянув в созданный нами файл, мы увидим следующее:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co1&quot;&gt;/////////////////////////////////////////////////////////&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;// This file was generated by OSAcfg_Tool utility.&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;// Do not modify it to prevent data loss on next editing.&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;// PROJECT NAME: Бегущие огни&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;// PLATFORM: HT-PICC 14-bit&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;/////////////////////////////////////////////////////////&lt;/span&gt;
&amp;nbsp;
&amp;nbsp;
&lt;span class=&quot;co2&quot;&gt;#ifndef _OSACFG_H&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define _OSACFG_H&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co2&quot;&gt;#define OS_TASKS               3&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define OS_DISABLE_PRIORITY&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define OS_ENABLE_TTIMERS&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co2&quot;&gt;#endif&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Конфигурация OSA&quot; [44774-51777] --&gt;
&lt;h2&gt;&lt;a name=&quot;прошивка_контроллера&quot; id=&quot;прошивка_контроллера&quot;&gt;Прошивка контроллера&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Теперь, когда текст нашей программы готов, нам нужно выполнить компиляцию и прошить полученный код в контроллер.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Прошивка контроллера&quot; [51778-52038] --&gt;
&lt;h3&gt;&lt;a name=&quot;сборка_проекта&quot; id=&quot;сборка_проекта&quot;&gt;Сборка проекта&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Для работы с проектом нам нужно иметь установленную интегрированную среду &lt;a href=&quot;http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&amp;amp;nodeId=1406&amp;amp;dDocName=en019469&amp;amp;part=SW007002&quot; class=&quot;urlextern&quot; title=&quot;http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&amp;amp;nodeId=1406&amp;amp;dDocName=en019469&amp;amp;part=SW007002&quot;  rel=&quot;nofollow&quot;&gt;MPLAB IDE&lt;/a&gt;, установленный компилятор &lt;a href=&quot;http://www.htsoft.com/microchip/products/compilers/picccompiler.php&quot; class=&quot;urlextern&quot; title=&quot;http://www.htsoft.com/microchip/products/compilers/picccompiler.php&quot;  rel=&quot;nofollow&quot;&gt;HI-TECH PICC STD&lt;/a&gt; (PRO-версия не подойдет).
&lt;/p&gt;

&lt;p&gt;
Скачиваем, если еще не скачали, &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/ref/download/intro&quot; class=&quot;wikilink1&quot; title=&quot;osa:ref:download:intro&quot;&gt;файлы операционной системы OSA&lt;/a&gt;&lt;/strong&gt;, распаковываем этот архив на диск C: (должна получиться папка C:\OSA).
&lt;/p&gt;

&lt;p&gt;
Распаковываем файл &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/lib/exe/fetch.php?media=http%3A%2F%2Fwiki.pic24.ru%2Flib%2Fexe%2Ffetch.php%2Fosa%2Flights.rar&quot; class=&quot;media mediafile mf_rar&quot; title=&quot;http://wiki.pic24.ru/lib/exe/fetch.php/osa/lights.rar&quot;&gt;lights.rar&lt;/a&gt;&lt;/strong&gt; в папку C:\TEST\PICKIT2. При этом внутри создастся папка LIGHTS. В MPLAB IDE открываем проект, в названии которого присутствует номер контроллера, который Вы собираетесь использовать: 886, 887, 690 или 88. Например, для демо-платы на базе 16F887 нам нужно открыть файл pk2_lights_887.mcp.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Примечание.&lt;/strong&gt;&lt;em&gt; При распаковке в другую папку, отличную от C:\TEST\PICKIT2\LIGHTS, нужно будет через меню Project\Build options…\Project в закладке Directories в списке include-путей заменить путь к файлам проекта на тот, куда Вы распаковали файлы из архива.&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
Выполняем сборку нажатием &lt;strong&gt;Ctrl+F10&lt;/strong&gt;.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Сборка проекта&quot; [52039-53832] --&gt;
&lt;h3&gt;&lt;a name=&quot;прошивка&quot; id=&quot;прошивка&quot;&gt;Прошивка&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Здесь все просто: 
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; подключаем программатор;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; в меню &amp;quot;Programmer\Select&amp;quot; programmer выбираем PicKit2;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; В настройках &amp;quot;Programmer→Settings&amp;quot; выбираем «3-State on «Release from Reset»&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; запускаем программирование &amp;quot;Programmer\Program&amp;quot;;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; освобождаем вывод MCLR &amp;quot;Programmer\Release from reset&amp;quot;.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Вот и все!
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Прошивка&quot; [53833-54296] --&gt;
&lt;h2&gt;&lt;a name=&quot;заключение&quot; id=&quot;заключение&quot;&gt;Заключение&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Итак, в данной статье была приведена демонстрация применения операционной системы реального времени OSA для написания программы &amp;quot;Бегущие огни&amp;quot;. Меньше чем за час мы спроектировали и написали программу, которая выполняет заданную задачу. При этом текст программы получился наглядным и легко читаемым. 
&lt;/p&gt;

&lt;p&gt;
Что хорошего мы извлекли из использования OSA:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; возможность организовать отдельные функциональные узлы программы в виде независимых и очень простых и наглядных задач;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; избавились от необходимости заводить несколько переменных для отсчета временных интервалов;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; мы не думали об обеспечении одновременного выполнения задач, т.к. все это на себя взяла ОС; кроме того, она это делает довольно эффективно, т.к. не запускает задачи, которые еще не готовы к выполнению.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Получившаяся программа может найти практическое применение, например, при оформлении вывески магазина. Для этого достаточно будет всего лишь применить кое-какие схемотехнические решения: в каждую цепь поставить не по одному светодиоду, а по несколько. Чередуя их четверками (или восьмерками, если программа для 16F887), т.е. сначала диод из первой цепи, затем из второй, потом из третей, дальше из четвертой, за ним - снова из первой, потом из второй и т.д., можно сделать длинную &amp;quot;гирлянду&amp;quot;, которой оформить вывеску, витрину, объявление.
&lt;/p&gt;

&lt;p&gt;
Введя небольшие изменения в программу, можно добавить свои эффекты над светодиодной картинкой, например, раскручивать ее с ускорением, или плавно гасить, а потом снова зажигать - в общем, как фантазия будет работать. Дерзайте!
&lt;/p&gt;

&lt;p&gt;

Спасибо за внимание.
&lt;/p&gt;

&lt;p&gt;
&lt;br/&gt;
 
&lt;br/&gt;
 
&lt;br/&gt;
 
&lt;/p&gt;

&lt;p&gt;
Виктор Тимофеев, март 2009&lt;br/&gt;
 
&lt;a href=&quot;mailto:&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot; class=&quot;mail JSnocheck&quot; title=&quot;&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot;&gt;osa@pic24.ru&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Заключение&quot; [54297-] --&gt;&lt;div class=&quot;footnotes&quot;&gt;
&lt;div class=&quot;fn&quot;&gt;&lt;sup&gt;&lt;a href=&quot;#fnt__1&quot; id=&quot;fn__1&quot; name=&quot;fn__1&quot; class=&quot;fn_bot&quot;&gt;1)&lt;/a&gt;&lt;/sup&gt; 
ОСРВ - операционная система реального времени&lt;/div&gt;
&lt;div class=&quot;fn&quot;&gt;&lt;sup&gt;&lt;a href=&quot;#fnt__2&quot; id=&quot;fn__2&quot; name=&quot;fn__2&quot; class=&quot;fn_bot&quot;&gt;2)&lt;/a&gt;&lt;/sup&gt; 
ШИМ - Широтно-Импульсная Модуляция&lt;/div&gt;
&lt;div class=&quot;fn&quot;&gt;&lt;sup&gt;&lt;a href=&quot;#fnt__3&quot; id=&quot;fn__3&quot; name=&quot;fn__3&quot; class=&quot;fn_bot&quot;&gt;3)&lt;/a&gt;&lt;/sup&gt; 
Планировщик - специальная подпрограмма ОСРВ, которая следит за готовностью задач к выполнению, выбирает наиболее приоритетную из них и передает ей управление&lt;/div&gt;
&lt;/div&gt;
</description>
    </item>
    <item rdf:about="http://www.pic24.ru/doku.php/osa/articles/pk2_osa_piano?rev=1275227741">
        <dc:format>text/html</dc:format>
        <dc:date>2010-05-30T17:55:41+03:00</dc:date>
        <title>Сенсорное пианино</title>
        <link>http://www.pic24.ru/doku.php/osa/articles/pk2_osa_piano?rev=1275227741</link>
        <description>


&lt;h1&gt;&lt;a name=&quot;сенсорное_пианино&quot; id=&quot;сенсорное_пианино&quot;&gt;Сенсорное пианино&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;

&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/pk2_osa_piano.jpg?id=osa%3Aarticles%3Apk2_osa_piano&quot; class=&quot;media&quot; title=&quot;osa:articles:pk2_osa_piano.jpg&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/pk2_osa_piano.jpg&quot; class=&quot;media&quot; title=&quot;pk2_osa_piano.jpg&quot; alt=&quot;pk2_osa_piano.jpg&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Сенсорное пианино&quot; [1-74] --&gt;
&lt;h2&gt;&lt;a name=&quot;введение&quot; id=&quot;введение&quot;&gt;Введение&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

В данной статье рассматривается возможность обработки сенсорной клавиатуры с применением АЦП. В качестве примера разработаем программу &amp;quot;Пианино&amp;quot;, обрабатывающую 36 сенсорных кнопок (3 октавы). Для интереса сделаем его многоголосым. В качестве аппаратной базы будем использовать демо-платы из набора pickit2 на базе контроллеров PIC16F690, PIC16F887 или PIC16F886. Программа будет работать под управлением ОСРВ OSA (прим.: ОСРВ - Операционная Система Реального Времени).
&lt;/p&gt;

&lt;p&gt;

Здесь 2-х минутное видео с демонстрацией того, что описывается в примере (пианист из меня, конечно, никакой).
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Введение&quot; [75-1127] --&gt;
&lt;h3&gt;&lt;a name=&quot;видео_hq_34_mb&quot; id=&quot;видео_hq_34_mb&quot;&gt;Видео HQ (34 Mb)&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

&lt;div align=center&gt;
&lt;object width=&quot;640&quot; height=&quot;480&quot;&gt;
&lt;param name=&quot;movie&quot; value=&quot;http://www.youtube.com/v/Ii7-orwG8F0&amp;hl=en&amp;fs=1&amp;rel=0&quot;&gt;&lt;/param&gt;
&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;&gt;&lt;/param&gt;

&lt;embed src=&quot;http://www.youtube.com/v/Ii7-orwG8F0&amp;hl=en&amp;fs=1&amp;rel=0&quot; type=&quot;application/x-shockwave-flash&quot; allowfullscreen=&quot;true&quot; width=&quot;640&quot; height=&quot;480&quot;&gt;&lt;/embed&gt;

&lt;/object&gt;
&lt;/div&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Видео HQ (34 Mb)&quot; [1128-1553] --&gt;
&lt;h3&gt;&lt;a name=&quot;видео_lq_6.3_mb&quot; id=&quot;видео_lq_6.3_mb&quot;&gt;Видео LQ (6.3 Mb)&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

&lt;div align=center&gt;
&lt;object width=&quot;320&quot; height=&quot;240&quot;&gt;
&lt;param name=&quot;movie&quot; value=&quot;http://www.youtube.com/v/1oic1seP8u8&amp;hl=en&amp;fs=1&amp;rel=0&quot;&gt;&lt;/param&gt;
&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;&gt;&lt;/param&gt;

&lt;embed src=&quot;http://www.youtube.com/v/1oic1seP8u8&amp;hl=en&amp;fs=1&amp;rel=0&quot; type=&quot;application/x-shockwave-flash&quot; allowfullscreen=&quot;true&quot; width=&quot;320&quot; height=&quot;240&quot;&gt;&lt;/embed&gt;

&lt;/object&gt;
&lt;/div&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Видео LQ (6.3 Mb)&quot; [1554-1979] --&gt;
&lt;h2&gt;&lt;a name=&quot;немного_теории&quot; id=&quot;немного_теории&quot;&gt;Немного теории&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Немного теории&quot; [1980-2020] --&gt;
&lt;h3&gt;&lt;a name=&quot;сенсорные_кнопки&quot; id=&quot;сенсорные_кнопки&quot;&gt;Сенсорные кнопки&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Основные принципы работы с сенсорными кнопками описаны &lt;strong&gt;&lt;a href=&quot;http://www.pickit2.ru/doku.php/проекты:touchsense&quot; class=&quot;urlextern&quot; title=&quot;http://www.pickit2.ru/doku.php/проекты:touchsense&quot;  rel=&quot;nofollow&quot;&gt;здесь&lt;/a&gt;&lt;/strong&gt;. Суть заключается в том, что когда мы прикасаемся пальцем к металлической пластине, мы вносим в схему дополнительную емкость. Это изменение емкости и фиксирует контроллер. Т.е. металлическая пластина является емкостным датчиком, представляющим собой конденсатор малой емкости. Если мы будем заряжать этот конденсатор через источник постоянного тока, то скорость нарастания напряжения на его обкладках будет пропорционально его емкости. Если измерение напряжения производить через одно и то же время после начала заряда конденсатора, то, очевидно, что при меньшем значении емкости конденсатор успеет зарядиться сильнее и, следовательно, напряжение на его обкладках будет выше.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/pk2_osa_piano_u_t_iconst.png?id=osa%3Aarticles%3Apk2_osa_piano&quot; class=&quot;media&quot; title=&quot;osa:articles:pk2_osa_piano_u_t_iconst.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/pk2_osa_piano_u_t_iconst.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Два конденсатора с емкостями C1 и C2 (C1 &amp;lt; C2) одновременно начали заряжать одинаковым током. На графике видно, что в момент времени t0 напряжение на конденсаторе C1 успело вырасти больше, чем на C2. Итак, мы знаем, что при касании металлической пластины (емкостного датчика) пальцем, мы добавляем в схему емкость. Если мы будем периодически заряжать/разряжать конденсатор емкостного датчика и измерять напряжение на его обкладках через одно и то же время после начала заряда (t0), то мы будем получать всегда одно и то же значение (оно будет меняться от измерения к измерению, но в малых пределах). Если же мы коснемся емкостного датчика пальцем, то его емкость возрастет, и при измерении напряжения через время t0 мы обнаружим, что его значение немного меньше обычного. 
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Этот принцип, т.е. измерение напряжения на емкостном датчике через одинаковое время после начала его заряда, мы и будем использовать для чтения состояния сенсорных кнопок.&lt;/strong&gt;
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;практическая_реализация&quot; id=&quot;практическая_реализация&quot;&gt;Практическая реализация&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Итак, для начала нам нужен емкостной датчик. В качестве него нам подойдет любая металлическая пластина. Далеко ходить не будем, и воспользуемся примером из статьи по приведенной выше ссылке, а именно - используем в качестве пластины монету. 
&lt;/p&gt;

&lt;p&gt;
Теперь нам нужен источник тока. В идеале хотелось бы иметь источник постоянного тока (в контроллерах PIC24FJ256GA110 и PIC24FJ256GB106 со встроенным модулем CTMU делается именно так, т.е. заряд конденсатора в емкостном датчике производится постоянным током). При малом количестве кнопок можно было сделать именно так. Но у нас много кнопок (36), и такое решение было бы громоздким. Можно ли обойтись без источника постоянного тока? Можно ли использовать RC-цепочку для заряда конденсатора емкостного датчика? Рассмотрим рисунок:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/pk2_osa_piano_u_t_.png?id=osa%3Aarticles%3Apk2_osa_piano&quot; class=&quot;media&quot; title=&quot;osa:articles:pk2_osa_piano_u_t_.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/pk2_osa_piano_u_t_.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Конденсаторы C1 и C2 (C1 &amp;lt; C2) стоят в RC-цепочках с одинаковыми значениями сопротивлений резисторов. Очевидно, что конденсатор C1 будет заряжаться быстрее, чем C2. И хоть графики заряда и нелинейные, мы все равно четко сможем определить, в каком случае емкость больше, т.к. через одинаковое время напряжения на конденсаторах будут разные. Для этого мы определим некий &amp;quot;порог срабатывания&amp;quot;, заданный значением напряжения Uп. Так что мы можем позволить себе сделать допуск и вместо источника тока воспользоваться RC-цепочкой.
&lt;/p&gt;

&lt;p&gt;
Далее для практической реализации нам нужно отмерять одинаковый временной интервал, после которого будет производиться измерение (t0). Здесь все достаточно просто: учитывая, что время t0 очень мало (единицы микросекунд), то задержку можно формировать программно пустым циклом. Единственное, о чем нельзя забывать, это запрет прерываний на время формирования задержки.
&lt;/p&gt;

&lt;p&gt;
Наконец, последнее, что нам нужно сделать, - это измерение напряжения. Тут мы воспользуемся встроенным в контроллер АЦП. Рассмотрим рисунок:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/pk2_osa_piano_u_t_measure.png?id=osa%3Aarticles%3Apk2_osa_piano&quot; class=&quot;media&quot; title=&quot;osa:articles:pk2_osa_piano_u_t_measure.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/pk2_osa_piano_u_t_measure.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Справа схема управления емкостным датчиком. Контроллер устанавливает выход DOUT в &amp;quot;1&amp;quot;, чтобы начать заряжать конденсатор через задающий резистор. На входе AIN производится измерение напряжения. Чтобы конденсатор был всегда гарантированно разряжен, вывод контроллера AIN почти всегда настроен как цифровой выход, установленный в &amp;quot;0&amp;quot;. Для снижения энергопотребления, чтобы, пока нет измерения, через резистор не тек ток, выход DOUT тоже устанавливается в &amp;quot;0&amp;quot;. Когда нужно произвести измерение емкости конденсатора, мы делаем следующую последовательность действий:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Начинаем измерение в точке t. До этого момента напряжение на конденсаторе = 0, т.к. AIN настроена как цифровой выход, установленный в &amp;quot;0&amp;quot;.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; В момент времени t устанавливаем DOUT в &amp;quot;1&amp;quot;, а AIN настраиваем как аналоговый вход.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Выдерживаем паузу до точки t0, чтобы дать конденсатору немного зарядиться.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; В момент времени t0 начинаем АЦ-преобразование установкой бита GODONE. При этом внутри контроллера происходит &amp;quot;защелкивание&amp;quot; напряжения на внутреннем конденсаторе удержания Chold (график напряжения на нем показан серым цветом; скорость заряда Chold будет всегда чуть ниже из-за наличия последовательного сопротивления внутри контроллера).&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; В момент t1, когда преобразование закончено, устанавливаем вывод AIN на выход и записываем в него &amp;quot;0&amp;quot;, чтобы разрядить конденсатор.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; После этого вывод DOUT возвращается в &amp;quot;0&amp;quot;.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

В результате для подключения одной кнопки мы имеем следующую схему:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/pk2_osa_piano_1button_scheme.png?id=osa%3Aarticles%3Apk2_osa_piano&quot; class=&quot;media&quot; title=&quot;osa:articles:pk2_osa_piano_1button_scheme.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/pk2_osa_piano_1button_scheme.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
В качестве емкостного датчика выступает монета. Пока мы ее не трогаем пальцем, мы будем производить измерение емкости Cp, являющейся суммой паразитной емкости входа AIN, емкости между монетой и землей, емкостью между проводниками на плате (или проводами, подводящими монеты к плате). Когда мы касаемся монеты пальцем, мы добавляем еще емкость Ch (human), в результате чего измеряемая емкость увеличивается.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;много_кнопок&quot; id=&quot;много_кнопок&quot;&gt;Много кнопок&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

По приведенному выше рисунку легко сделать вывод, что на каждый аналоговый вход контроллера можно повесить по монетке. 
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/pk2_osa_piano_nbuttons_scheme.png?id=osa%3Aarticles%3Apk2_osa_piano&quot; class=&quot;media&quot; title=&quot;osa:articles:pk2_osa_piano_nbuttons_scheme.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/pk2_osa_piano_nbuttons_scheme.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Но так мы получим максимум столько кнопок, сколько АЦП имеется на борту нашего контроллера (например, 12 у PIC16F690). А нам бы хотелось еще больше, т.к. пианино с одной октавой будет выглядеть довольно ущербно. Хотелось бы хотя бы 3 октавы (можно сделать и больше, но пока остановимся на трех). Нам нужно модифицировать схему так, чтобы один АЦП-вход имел возможность измерять напряжение на нескольких кнопках. Как же это сделать? Ответ прост: ставить разделяющие диоды.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/pk2_osa_piano_2buttons_scheme.png?id=osa%3Aarticles%3Apk2_osa_piano&quot; class=&quot;media&quot; title=&quot;osa:articles:pk2_osa_piano_2buttons_scheme.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/pk2_osa_piano_2buttons_scheme.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Из рисунка видно, что, управляя цифровыми выходами DOUT1 и DOUT2, мы сможем входом AIN измерять отдельно емкость то одного, то другого емкостного датчика. Таким образом, на один аналоговый вход мы можем завести столько кнопок, сколько захотим. Ограничены мы лишь тем, сколько выводов контроллера мы сможем использовать как управляющие выходы DOUT. Комбинируя аналоговые входы и цифровые выходы, мы строим матрицу сенсорных кнопок. Очевидно, что сколько бы мы не выделили выводов под клавиатуру, максимальное количество кнопок получится, если количество аналоговых входов будет равно (или на 1 отличаться, если у нас нечетное количество выводов) количеству цифровых выходов. В рассмотренном примере &amp;quot;Пианино&amp;quot; для управления клавиатурой выделено 12 выводов, из них 6 - аналоговые входы и 6 - цифровые выходы. Получается матрица 6х6 = 36 кнопок.
&lt;/p&gt;

&lt;p&gt;
В результате получаем следующую схему включения кнопок:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/pk2_osa_piano_c-matrix.png?id=osa%3Aarticles%3Apk2_osa_piano&quot; class=&quot;media&quot; title=&quot;osa:articles:pk2_osa_piano_c-matrix.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/pk2_osa_piano_c-matrix.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;примечание&quot; id=&quot;примечание&quot;&gt;Примечание&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Хоть описанный подход и был успешно реализован для обработки клавиатуры из 36 кнопок, он не лишен недостатков, которые следует предусматривать при проектировании устройства с использованием данного подхода.
&lt;/p&gt;

&lt;/div&gt;

&lt;h5&gt;&lt;a name=&quot;температурный_дрейф_rc-цепочки&quot; id=&quot;температурный_дрейф_rc-цепочки&quot;&gt;Температурный дрейф RC-цепочки&lt;/a&gt;&lt;/h5&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Сопротивление резистора в цепи емкостного датчика изменяется при изменении температуры. Так же меняется емкость внутреннего удерживающего конденсатора АЦП контроллера (Chold). Это приводит к тому, что крутизна графика зарядки емкостного датчика будет меняться с изменением температуры окружающей среды. 
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/pk2_osa_piano_rc_temp.png?id=osa%3Aarticles%3Apk2_osa_piano&quot; class=&quot;media&quot; title=&quot;osa:articles:pk2_osa_piano_rc_temp.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/pk2_osa_piano_rc_temp.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
На рисунке показаны графики заряда RC-цепочки нажатой и не нажатой кнопки при сильно отличающихся температурах. Видно, что скорость заряда емкостного датчика без добавленной емкости Ch при температуре T2 приблизительно такая же, как и с добавлением Ch при температуре T1. Поэтому при проектировании устройств с емкостными сенсорными кнопками следует учитывать, что порог срабатывания кнопки нужно автоматически перенастраивать с каким-то интервалом (хотя бы раз в сутки).
&lt;/p&gt;

&lt;/div&gt;

&lt;h5&gt;&lt;a name=&quot;наводки_при_прикосновении&quot; id=&quot;наводки_при_прикосновении&quot;&gt;Наводки при прикосновении&lt;/a&gt;&lt;/h5&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Когда мы касаемся пальцем металлической пластины, мы вносим в схему не только дополнительную емкость, но и источник помех. Поэтому в реальности график заряда Ch будет выглядеть так:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/pk2_osa_piano_u_t_navodki.png?id=osa%3Aarticles%3Apk2_osa_piano&quot; class=&quot;media&quot; title=&quot;osa:articles:pk2_osa_piano_u_t_navodki.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/pk2_osa_piano_u_t_navodki.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Голубым цветом изображен график без наводок, и для него напряжение в момент времени t0 будет равным Uи (идеальное). В реальности график будет искажен помехами от наводок (синий график). И реальное напряжение U в момент t0 будет всегда изменяться в некоторых пределах вокруг Uи, и оно может быть как больше Uи, так и меньше, в зависимости от фазы наводок в момент защелкивания напряжения на Chold. Эти наводки не будут нам мешать, если мы касаемся датчика непосредственно, т.к. в этом случае мы вносим в схему сравнительно большую емкость, и угол наклона графика изменится существенно. Но, если касаемся датчика через какую-то преграду (например, стекло или бумага), то вносимая в схему емкость будет мала и график отклонится хоть и заметно, но очень не сильно. А помехи будут его еще искажать, причем иногда так, что измеряемое напряжение может оказаться не только выше порогового, но и сильно приблизиться к измеренному без прикосновения напряжению, что затруднит определение факта нажатия кнопки.
&lt;/p&gt;

&lt;p&gt;
Поэтому, если предполагается использование какой-то прокладки между емкостным датчиком и пальцем, то следует предусмотреть многократное считывание датчика и вычисление среднего значения. Это позволит более точно определить, было ли нажатие.
&lt;/p&gt;

&lt;/div&gt;

&lt;h5&gt;&lt;a name=&quot;паразитная_емкость_на_входах&quot; id=&quot;паразитная_емкость_на_входах&quot;&gt;Паразитная емкость на входах&lt;/a&gt;&lt;/h5&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Все входы контроллера обладают некоей паразитной емкостью, которая также принимает участие в работе нашей RC-цепочки. Эта емкость - единицы пикофарад, однако в нашем случае ее влияние будет ощутимо. Проблема в том, что разные выводы контроллера имеют различные схемотехнические особенности: у них разная периферия, некоторые выводы имеют встроенный МОП-транзистор для обеспечения pull-up подтяжки (он даже в отключенном состоянии внесет свою толику в паразитную емкость) и т.п. Поэтому в программе следует предусмотреть то, что порог срабатывания на разных АЦП-входах будет различным. 
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Сенсорные кнопки&quot; [2021-20514] --&gt;
&lt;h3&gt;&lt;a name=&quot;генерация_звука&quot; id=&quot;генерация_звука&quot;&gt;Генерация звука&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;генерация_низкочастотным_меандром&quot; id=&quot;генерация_низкочастотным_меандром&quot;&gt;Генерация низкочастотным меандром&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Самый распространенный способ генерации звука во встраиваемых системах - генерация меандра частоты, соответствующей частоте самого звука. Например, для генерации тона 1 КГц, мы формируем на выводе контроллера прямоугольный меандр частотой 1 КГц. В большинстве случаев такого способа вывода звука достаточно. Например, микроволновка сообщает нам, что разогрев окончен; брелок автомобильной сигнализации проигрывает мелодию при постановке/снятии с охраны; холодильник предупреждает нас, что мы забыли закрыть дверь и т.д. 
&lt;/p&gt;

&lt;p&gt;
Такой способ прост, требует минимум ресурсов контроллера, достаточно информативен: можно давать звуки разной длительности, частоты, комбинации частот и пр. Можно ли таким способом получить многоголосье? В общем-то, можно, если на каждый звуковой канал выводить меандр своей частоты, а затем все эти меандры схематически суммировать и подавать на динамик. Однако тут есть несколько недостатков: во-первых, мы теряем несколько выводов микросхемы; во-вторых, сгенерировать два меандра разной частоты - это задача на порядок сложнее, чем сгенерировать один меандр (что сводит на нет преимущество в простоте реализации); наконец, в-третьих, звук получится очень неинтересным и даже немного раздражающим: из-за крутых фронтов прямоугольного сигнала звук будет очень резким.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;генерация_с_помощью_цап&quot; id=&quot;генерация_с_помощью_цап&quot;&gt;Генерация с помощью ЦАП&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Т.к. у PIC-контроллеров нет встроенного ЦАП-модуля, мы можем воспользоваться модулем ШИМ, работающим на высокой частоте. С его помощью мы сможем сгенерировать сигнал практически любой формы в заданном частотном диапазоне (частотный диапазон будет ограничен сверху в основном за счет частоты семплирования, об этом - ниже).
&lt;/p&gt;

&lt;/div&gt;

&lt;h5&gt;&lt;a name=&quot;генерация_одноканального_сигнала&quot; id=&quot;генерация_одноканального_сигнала&quot;&gt;Генерация одноканального сигнала&lt;/a&gt;&lt;/h5&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Рассмотрим генерацию сигнала прямоугольной формы с помощью ШИМ. 
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/pk2_osa_piano_pwm1.png?id=osa%3Aarticles%3Apk2_osa_piano&quot; class=&quot;media&quot; title=&quot;osa:articles:pk2_osa_piano_pwm1.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/pk2_osa_piano_pwm1.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Когда мы генерируем &amp;quot;1&amp;quot;, скважность импульсов ШИМ = 1 (на рисунке взята скважность чуть больше для наглядности, чтобы было видно, что частота ШИМ выше частоты генерируемого сигнала). Когда мы генерируем &amp;quot;0&amp;quot;, скважность импульсов ШИМ максимальна (в идеале скважность равна бесконечности, т.е. импульсы отсутствуют, но на рисунке, опять же, для наглядности изображены просто импульсы большой скважности).
&lt;/p&gt;

&lt;p&gt;
Пропустив цифровой сигнал с выхода ШИМ-модулятора через НЧ-фильтр, мы получим сигнал &amp;quot;прямоугольной формы&amp;quot; - зеленый график (в реальности он будет больше похож на прямоугольный, поскольку частота ШИМ гораздо выше приведенной на графике; по этой же причине пульсации будут гораздо меньше). В качестве НЧ-фильтра в самом простом случае может выступать RC-цепочка.
&lt;/p&gt;

&lt;/div&gt;

&lt;h5&gt;&lt;a name=&quot;генерация_двухканального_сигнала&quot; id=&quot;генерация_двухканального_сигнала&quot;&gt;Генерация двухканального сигнала&lt;/a&gt;&lt;/h5&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Теперь перед нами стоит задача сгенерировать сразу два прямоугольных сигнала различной частоты, наложенных друг на друга. Когда мы генерировали один прямоугольный сигнал, мы приняли единичное состояние за максимальное значение (скважность импульсов ШИМ = 1), а нулевое - за минимальное (скважность = бесконечности). Но теперь нам нужно сгенерировать сигнал, равный сумме двух меандров, и за максимальное значение будет принято максимально возможное при таком суммировании, то есть то состояние, при котором и первый и второй сигналы находятся в &amp;quot;1&amp;quot;. На рисунке схематически приведен пример использования ШИМ для генерации сразу двух меандров разной частоты.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/pk2_osa_piano_pwm2.png?id=osa%3Aarticles%3Apk2_osa_piano&quot; class=&quot;media&quot; title=&quot;osa:articles:pk2_osa_piano_pwm2.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/pk2_osa_piano_pwm2.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
На рисунке видно, что на тех участках, где &amp;quot;1&amp;quot; установлена только на одном из каналов, напряжение на выходе фильтра равно половине напряжения питания (скважность импульсов ШИМ-сигнала на этих участках равна двум). 
&lt;/p&gt;

&lt;p&gt;
Теперь, когда стало ясно, как генерировать двухканальный звук, мы можем сгенерировать и трех- и четырех- и десятиканальный. 
&lt;/p&gt;

&lt;/div&gt;

&lt;h5&gt;&lt;a name=&quot;генерация_синуса&quot; id=&quot;генерация_синуса&quot;&gt;Генерация синуса&lt;/a&gt;&lt;/h5&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Генерация синусоидального сигнала, по сути, не отличается от генерации, описанной в предыдущем параграфе. Из графика синуса мы делаем точечные выборки с частотой, равной частоте ШИМ, и устанавливаем скважность текущего импульса соответствующей текущей точке выборки. Например, если мы взяли точку в самом пике синусоиды, то скважность будет равна 1; если мы взяли точку в середине периода (соответствующую 180 градусам), то скважность импульсов ШИМ-сигнала будет равна 2; если точка соответствует 45 градусам, то скважность будет равна (1 + 2^-2) ~ 2.41. Ниже приведен рисунок, показывающий генерацию синусоидального сигнала с помощью высокочастотной ШИМ:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/pk2_osa_piano_pwm3.png?id=osa%3Aarticles%3Apk2_osa_piano&quot; class=&quot;media&quot; title=&quot;osa:articles:pk2_osa_piano_pwm3.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/pk2_osa_piano_pwm3.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Ну и, само собой, теперь для нас не проблема сформировать сигнал, являющийся суммой двух синусоид:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/pk2_osa_piano_pwm4.png?id=osa%3Aarticles%3Apk2_osa_piano&quot; class=&quot;media&quot; title=&quot;osa:articles:pk2_osa_piano_pwm4.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/pk2_osa_piano_pwm4.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
трех синусоид, пяти синусоид, двух синусоид и трех прямоугольников и т.д.
&lt;/p&gt;

&lt;p&gt;
На этом введение в теорию закончим и приступим к реализации задуманного.

&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Генерация звука&quot; [20515-28886] --&gt;
&lt;h2&gt;&lt;a name=&quot;проектирование_программы&quot; id=&quot;проектирование_программы&quot;&gt;Проектирование программы&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Зададимся задачей разработать программу &amp;quot;Пианино&amp;quot;, которая будет способна воспроизводить до 8-ми нот одновременно и обрабатывать клавиатуру из 36 клавиш. Для разнообразия сделаем так, чтобы пианино могло синтезировать звуки различных тембров (тембр будет меняться нажатием кнопки).
&lt;/p&gt;

&lt;p&gt;
Звук мы будет выводить через аппаратный ШИМ на максимально возможной частоте при разрешении 8 бит. При тактовой частоте контроллера, равной 20 МГц, максимальная частота 8-разрядного ШИМ будет 78 КГц. Частоту семплирования синтезатора выберем равной 20 КГц (хотелось бы выше, но 8 каналов звука быстрее не обработать).
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Проектирование программы&quot; [28887-30038] --&gt;
&lt;h3&gt;&lt;a name=&quot;задачи&quot; id=&quot;задачи&quot;&gt;Задачи&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Сперва определимся с тем, какие задачи предстоит решать нашему контроллеру. Здесь же определимся с тем, какие из них оформим в виде задач ОСРВ, а какие поместим в прерывание.

&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Опрос клавиатуры - в этой задаче мы будем опрашивать 36 кнопок сенсорной клавиатуры по приведенной выше методике;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Генерация звука (синтезатор) - формирование скважности импульсов ШИМ в соответствие с нажатыми клавишами и выбранным инструментом.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Опрос кнопки - здесь будем ждать нажатия кнопки, и когда она будет нажата, будем выбирать новый музыкальный инструмент для синтезатора.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Задача генерации звука критична ко времени выполнения, т.к. у нас частота семплирования (т.е. изменения скважности ШИМ-сигнала) 20 КГц, то на один период выполнения задачи у нас максимум 50 мкс или 250 тактов (на самом деле еще меньше, т.к. нам нужно успевать и другие задачи обрабатывать). Есть смысл разбить эту задачу на две: 
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; первая будет заниматься исключительно формированием ШИМ-сигнала и будет помещена в прерывание (к ней и будет относиться требование уместиться в 250 тактов);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; вторая будет работать в фоновом режиме как обычная задача ОСРВ и будет заниматься формированием целеуказаний для первой в соответствие с нажатыми клавишами.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Опрос клавиатуры и опрос кнопки мы оформим в виде задач ОСРВ, т.к. они некритичны к скорости.
&lt;/p&gt;

&lt;p&gt;
Итак, у нас получились 3 ОСРВ-задачи: &amp;quot;клавиатура&amp;quot;, &amp;quot;кнопка&amp;quot; и &amp;quot;формирователь звуковых переменных&amp;quot; - и одна не ОСРВ-задача - &amp;quot;синтезатор&amp;quot;, - которая будет помещена в прерывание.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;клавиатура&quot; id=&quot;клавиатура&quot;&gt;&amp;quot;Клавиатура&amp;quot;&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

В функции этой задачи будут входить опрос всех 36 емкостных датчиков и формирование переменной состояния клавиатуры. Кроме того, задача должна будет как-то сообщать остальной программе, что состояние клавиатуры изменилось (либо что-то было нажато, либо что-то было отпущено).
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;кнопка&quot; id=&quot;кнопка&quot;&gt;&amp;quot;Кнопка&amp;quot;&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Эта задача должна постоянно опрашивать состояние кнопки (с подавлением дребезга). При обнаружении факта нажатия кнопки должна будет происходить смена инструмента.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;формирователь_данных_для_синтезатора&quot; id=&quot;формирователь_данных_для_синтезатора&quot;&gt;&amp;quot;формирователь данных для синтезатора&amp;quot;&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Эта задача будет ожидать сообщения о нажатии/отпускании клавиш и формировать данные для синтезатора. Эти данные - по какому из восьми каналов генерируется звук для какой клавиши, или какой канал молчит.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;синтезатор&quot; id=&quot;синтезатор&quot;&gt;&amp;quot;Синтезатор&amp;quot;&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Для синтезирования звука в программе записан оцифрованный период звуковой волны (64 точки) в виде массива. Когда нужно генерировать звук, &amp;quot;синтезатор&amp;quot; при каждом запуске (1 раз в 50 мкс) выбирает из массива очередное значение и на основании него формирует скважность импульса для ШИМ-генератора. Массив оцифрованных значений  звуковой волны рассматривается &amp;quot;синтезатором&amp;quot; как кольцевой, т.е. как бесконечный синус. Чем выше частота ноты, которую нам нужно синтезировать, тем с большим шагом выбираются значения из таблицы. 
&lt;/p&gt;

&lt;p&gt;
Когда нужно генерировать сразу два канала, из массива оцифрованного периода выбираются очередные значения для каждого канала по отдельности с шагами, соответствующими синтезируемым нотам. После этого прочитанные из таблицы значения суммируются, и на основании суммы формируется скважность импульсов ШИМ-генератора. Та же схема и для трех, четырех и т.д. каналов.
&lt;/p&gt;

&lt;p&gt;
Мы предусмотрим в нашей программе синтезирование 4 различных инструментов, поэтому и массивов с данными об оцифрованных периодах в программе должно быть 4.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Задачи&quot; [30039-36001] --&gt;
&lt;h3&gt;&lt;a name=&quot;данные&quot; id=&quot;данные&quot;&gt;Данные&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Здесь ответим себе на два вопроса:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Какими данными будет оперировать наша программа?&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Какими данными и как будут обмениваться задачи?&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;для_клавиатуры&quot; id=&quot;для_клавиатуры&quot;&gt;Для клавиатуры&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Нам потребуется некий массив для хранения информации о нажатых кнопках. Кнопок у нас 36, значит, это должен быть массив размерностью как минимум 5 байт. Далее, для индивидуальной установки/сброса бита для каждой клавиши нам нужна какая-то переменная для адресации конкретного бита в массиве. Проще всего для этой цели завести две переменные, одна из которых будет указывать на байт в массиве, а вторая будет являться маской бита в байте. 
&lt;/p&gt;

&lt;p&gt;
Кроме того, как уже было описано выше, пороги срабатывания клавиш могут меняться со временем, нам нужно иметь переменные для хранения порогов. И т.к. для разных АЦП-входов эти пороги могут различаться, то на каждый вход должна быть своя переменная, т.е. должен быть массив из 6 значений. 
&lt;/p&gt;

&lt;p&gt;
Все эти данные будут сведены в структуру:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;struct&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;   Data&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;KBD_SIZE&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;         &lt;span class=&quot;co1&quot;&gt;// Массив битов: 1 - кнопка нажата.&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;   cDataPos;               &lt;span class=&quot;co1&quot;&gt;// Две переменные - указатель&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;   cDataMask;              &lt;span class=&quot;co1&quot;&gt;// бита при приеме.&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;   Porogs&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;KBD_COLUMNS&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;    &lt;span class=&quot;co1&quot;&gt;// Массив пороговых значений для&lt;/span&gt;
                                            &lt;span class=&quot;co1&quot;&gt;// определения, нажата ли кнопка&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; KBD;&lt;/pre&gt;
&lt;p&gt;
&lt;em&gt;&lt;strong&gt;Примечание.&lt;/strong&gt; Мы предусмотрительно пользуемся константами KBD_SIZE (=36) и KBD_COLUMNS (=6), оставляя себе возможность минимальными силами увеличить или сократить количество клавиш.&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
Теперь, мы должны помнить, программа &amp;quot;Пианино&amp;quot; проектируется для разных контроллеров, а у разных контроллеров будут использованы разные выводы для матрицы клавиатуры. Поэтому нам нужно предусмотреть какой-нибудь удобный способ адресации этих выводов. Предположим, что в матрице в столбцах указываются аналоговые входы, а в строках - управляющие выходы. Рассмотрим, какие данные нам нужны для описания строк и столбцов. Со строками (управляющими выходами) все просто: нужны только адрес порта и маска бита в порту, по которой соответствующий вывод будет устанавливаться либо в &amp;quot;1&amp;quot;, либо в &amp;quot;0&amp;quot;. Со столбцами (аналоговыми входами), учитывая нашу методику, немного сложнее: нам нужен, во-первых, номер АЦП-канала, во-вторых, указатели на PORT и TRIS регистры, поскольку нам придется управлять и тем и другим, и, наконец, в-третьих, - маска бита в порту. Таким образом, мы формируем две структуры для шаблонов:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;struct&lt;/span&gt;          &lt;span class=&quot;co1&quot;&gt;// Тип для задания аналогового входа&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;                       &lt;span class=&quot;co1&quot;&gt;//&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;  cADCChannel;  &lt;span class=&quot;co1&quot;&gt;// Номер аналогового канала&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;pPort;        &lt;span class=&quot;co1&quot;&gt;// Указатель на регистр PORT&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;pTris;        &lt;span class=&quot;co1&quot;&gt;// Указатель на регистр направления&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;  cMask;        &lt;span class=&quot;co1&quot;&gt;// Маска бита в порту&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; TColumn;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;struct&lt;/span&gt;          &lt;span class=&quot;co1&quot;&gt;// Тип для управляющего выхода&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;                       &lt;span class=&quot;co1&quot;&gt;//&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;pPort;        &lt;span class=&quot;co1&quot;&gt;// Указатель на регистр PORT&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;  cMask;        &lt;span class=&quot;co1&quot;&gt;// Маска бита в порту&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; TRow;&lt;/pre&gt;
&lt;p&gt;
Теперь через эти типы можно объявлять массивы выводов контроллера, образующих строки и столбцы матрицы кнопок.
&lt;/p&gt;

&lt;p&gt;
&lt;em&gt;&lt;strong&gt;Примечание.&lt;/strong&gt; Учитывая, что обе переменные из типа TRow присутствуют в типе TColumn, можно было бы обойтись одним типом, просто для строк поля cADCChannel и pTris заполнять нулями, но для строгости мы будем использовать различные типы.&lt;/em&gt;
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;для_синтезатора&quot; id=&quot;для_синтезатора&quot;&gt;Для синтезатора&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Синтезатору для работы потребуется массив значений оцифрованных периодов синусоид для различных инструментов. Эти массивы будут храниться в программной памяти в виде констант (см. файл &lt;strong&gt;sinus.c&lt;/strong&gt;). Т.к. скорость работы самого синтезатора хотелось бы максимально увеличить, то воспользуемся той особенностью, что обращение к массиву в RAM  происходит быстрее, чем обращение к массиву в ROM. Поэтому для работы с массивом значений оцифрованного периода данные для текущего инструмента мы будем копировать в RAM. Т.е. нам нужно зарезервировать в RAM-памяти массив для этих целей:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; Sample&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Т.к. у нас предусмотрено синтезирование четырех инструментов, то в программе должна быть переменная, показывающая номер текущего инструмента. За выбор инструмента отвечает задача &amp;quot;Кнопка&amp;quot;, в которой мы и будем производить копирование из ROM в RAM. Есть смысл сделать переменную, обозначающую номер текущего инструмента, статической внутри этой задачи:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; s_cCurSample;&lt;/pre&gt;
&lt;p&gt;
Теперь, синтезатор должен знать, какой канал &amp;quot;молчит&amp;quot;, а по какому воспроизводится звук, причем ему нужно указать и частоту звука, и текущую фазу. Итак, у нас получается структура:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;struct&lt;/span&gt;          &lt;span class=&quot;co1&quot;&gt;// Для переменных управления звуком&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt; F;     &lt;span class=&quot;co1&quot;&gt;// Частота&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt; f;     &lt;span class=&quot;co1&quot;&gt;// Фаза&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; key;  &lt;span class=&quot;co1&quot;&gt;// Клавиша, которая проигрывается в данный момент. (0 - молчит)&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; TSound;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Данные&quot; [36002-43938] --&gt;
&lt;h2&gt;&lt;a name=&quot;реализация&quot; id=&quot;реализация&quot;&gt;Реализация&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Реализация&quot; [43939-43972] --&gt;
&lt;h3&gt;&lt;a name=&quot;кнопка1&quot; id=&quot;кнопка1&quot;&gt;Кнопка&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Как мы уже писали, по нажатию кнопки должен изменяться инструмент. Не будем забывать, что пользователь может и не менять инструмент, т.е. кнопку вообще не будет трогать, следовательно, какой-то инструмент нужно загрузить в самом начале.
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task_Button &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; s_cCurSample;
&amp;nbsp;
    s_cCurSample &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
&amp;nbsp;
    CopySample&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;s_cCurSample&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  Ожидаем нажатия на кнопку (с устранением дребезга)&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
            OS_Cond_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;pin_BUTTON&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
            OS_Stimer_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;ST_BUTTON, &lt;span class=&quot;nu0&quot;&gt;40&lt;/span&gt; ms&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;pin_BUTTON&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  Изменяем номер инструмента и копируем данные об инструменте в&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  массив Sample&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
        s_cCurSample++;
        s_cCurSample &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;;
        CopySample&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;s_cCurSample&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  Ждем отпускания кнопки&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
            OS_Cond_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;pin_BUTTON&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
            OS_Stimer_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;ST_BUTTON, &lt;span class=&quot;nu0&quot;&gt;40&lt;/span&gt; ms&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;pin_BUTTON&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&amp;nbsp;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Обратим внимание на то, что для формирования задержек используется не таймер задач (т.е. не сервис &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay), а статический таймер. Это связано с тем, что код синтезатора, находящийся в прерывании вместе с системным обработчиком таймеров, может долго выполняться, когда активны все 8 каналов. И так как нам гарантированно нужно вместиться в 50 мкс (250 тактов), то любое ускорение кода приветствуется. В данном случае для ускорения применены статические таймеры. Дело тут не в том, что инкремент статического таймера производится быстрее, чем таймера задачи, а в том, что таймеры нужны только двум задачам из трех активных. И избавляясь от обработки одного неиспользуемого таймера, мы выигрываем драгоценные такты.
&lt;/p&gt;

&lt;p&gt;
Переменная &lt;strong&gt;s_cCurSample&lt;/strong&gt; описана как static, т.к. нам важно сохранение ее значения после переключения на другие задачи. Функция &lt;strong&gt;CopySample&lt;/strong&gt; просто копирует массив из ROM-памяти в массив Sample:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; CopySample &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; c&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; n;
    c &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;n &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; n &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;64&lt;/span&gt;; n&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; Sample&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;n&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; SAMPLES&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;c&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;n&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Кнопка&quot; [43973-47583] --&gt;
&lt;h3&gt;&lt;a name=&quot;клавиатура1&quot; id=&quot;клавиатура1&quot;&gt;Клавиатура&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Задача чтения состояния клавиатуры каждые 10 мс опрашивает все 6 строк с кнопками. Перед первым опросом обнуляются все значения порогов для всех столбцов (аналоговых входов). Подпрограмма чтения строки проверяет эти переменные и, если они нулевые, сохраняет туда считанные с аналоговых входов значения за вычетом 15% барьера. 
&lt;/p&gt;

&lt;p&gt;
&lt;em&gt;&lt;strong&gt;Примечание.&lt;/strong&gt; При использовании прокладки между пальцем и пластиной емкостного датчика барьер должен стоять выше.&lt;/em&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task_Keyboard &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; n;
    &lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; s_cChanged;
&amp;nbsp;
    &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//  Один раз выполняем чтение, чтобы точно быть уверенными в том, что все&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//  TRIS'ы аналоговых входов установлены в &amp;quot;0&amp;quot; (на выход) для разряда&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//  конденсаторов&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
    ReadRow&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//  При первом запуске все пороги устанавливаем в 0&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;n &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; n &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; KBD_COLUMNS; n&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; KBD.&lt;span class=&quot;me1&quot;&gt;Porogs&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;n&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  Перед измерением состояния всех кнопок устанавливаем маску для первой&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  кнопки&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
        KBD.&lt;span class=&quot;me1&quot;&gt;cDataPos&lt;/span&gt;  &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;                  &lt;span class=&quot;co1&quot;&gt;// Номер байта&lt;/span&gt;
        KBD.&lt;span class=&quot;me1&quot;&gt;cDataMask&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x01&lt;/span&gt;;               &lt;span class=&quot;co1&quot;&gt;// Маска бита в байте&lt;/span&gt;
        s_cChanged &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;                     &lt;span class=&quot;co1&quot;&gt;// Признак того, что состояние какой-то&lt;/span&gt;
                                            &lt;span class=&quot;co1&quot;&gt;// клавиши было изменено&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;n &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; n &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; KBD_ROWS; n&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;      &lt;span class=&quot;co1&quot;&gt;// Цикл по всем строкам&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
            s_cChanged |= ReadRow&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;n&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;       &lt;span class=&quot;co1&quot;&gt;// Измерение всех датчиков в строке&lt;/span&gt;
                                            &lt;span class=&quot;co1&quot;&gt;// может длиться до 500 мкс, поэтому&lt;/span&gt;
                                            &lt;span class=&quot;co1&quot;&gt;// после прочтения каждой строки&lt;/span&gt;
                                            &lt;span class=&quot;co1&quot;&gt;// переключаем контекст&lt;/span&gt;
            OS_Yield&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  Если были изменения в кнопках, то отправляем сообщение задаче&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  формирования звуковых переменных&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;s_cChanged&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
            OS_Msg_Send_Now&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;msg_KBD, &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;OST_MSG&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; KBD.&lt;span class=&quot;me1&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
        OS_Stimer_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;ST_KEYBOARD, &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt; ms&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Здесь так же, как и в задаче &lt;strong&gt;Task_Button&lt;/strong&gt; для формирования задержки применен статический таймер.
&lt;/p&gt;

&lt;p&gt;
Теперь рассмотрим основную функцию клавиатуры, ту, которая занимается чтением состояний емкостных датчиков.
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; ReadRow &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; row&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;        m, a, i, k;             &lt;span class=&quot;co1&quot;&gt;// Вспомогательные переменные&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;        col;                    &lt;span class=&quot;co1&quot;&gt;// Текущий канал&lt;/span&gt;
    TColumn     Column;                 &lt;span class=&quot;co1&quot;&gt;// Для сокращения кода обработки канала его&lt;/span&gt;
                                        &lt;span class=&quot;co1&quot;&gt;// параметры копируются из ROM в переменную&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; s_Changes&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;KBD_SIZE&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;    &lt;span class=&quot;co1&quot;&gt;// Изменения в состояниях кнопок&lt;/span&gt;
                                        &lt;span class=&quot;co1&quot;&gt;// для подавления дребезга&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; bit  s_bChanged;             &lt;span class=&quot;co1&quot;&gt;// Переменная для возврата&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;ROWS&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;row&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;pPort&lt;/span&gt; |= ROWS&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;row&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;cMask&lt;/span&gt;;&lt;span class=&quot;co1&quot;&gt;// Управляющий выход для линии в &amp;quot;1&amp;quot;&lt;/span&gt;
&amp;nbsp;
    s_bChanged &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;                     &lt;span class=&quot;co1&quot;&gt;// Изначально считаем, что изменений&lt;/span&gt;
                                        &lt;span class=&quot;co1&quot;&gt;// не было&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//   Цикл по всем каналам&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;col &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; col  &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; KBD_COLUMNS; col&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;// Копируем параметры канала в переменную&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
        Column.&lt;span class=&quot;me1&quot;&gt;pPort&lt;/span&gt;       &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; COLUMNS&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;col&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;pPort&lt;/span&gt;;
        Column.&lt;span class=&quot;me1&quot;&gt;pTris&lt;/span&gt;       &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; COLUMNS&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;col&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;pTris&lt;/span&gt;;
        Column.&lt;span class=&quot;me1&quot;&gt;cMask&lt;/span&gt;       &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; COLUMNS&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;col&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;cMask&lt;/span&gt;;
        Column.&lt;span class=&quot;me1&quot;&gt;cADCChannel&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; COLUMNS&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;col&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;cADCChannel&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  Выбираем канал ADC&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
        CHS0 &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
        CHS1 &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
        CHS2 &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Column.&lt;span class=&quot;me1&quot;&gt;cADCChannel&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x01&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; CHS0 &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Column.&lt;span class=&quot;me1&quot;&gt;cADCChannel&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x02&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; CHS1 &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Column.&lt;span class=&quot;me1&quot;&gt;cADCChannel&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x04&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; CHS2 &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;co2&quot;&gt;#if defined(_16F887) || defined(_16F886) || defined(_16F690)&lt;/span&gt;
        CHS3 &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Column.&lt;span class=&quot;me1&quot;&gt;cADCChannel&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x08&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; CHS3 &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
        &lt;span class=&quot;co2&quot;&gt;#endif&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  Начинаем измерение&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
        GIE &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;                        &lt;span class=&quot;co1&quot;&gt;// На время заряда конденсатора блокируем&lt;/span&gt;
                                        &lt;span class=&quot;co1&quot;&gt;// прерывания, чтобы получить фиксированную&lt;/span&gt;
                                        &lt;span class=&quot;co1&quot;&gt;// паузу&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;Column.&lt;span class=&quot;me1&quot;&gt;pTris&lt;/span&gt; |=  Column.&lt;span class=&quot;me1&quot;&gt;cMask&lt;/span&gt;; &lt;span class=&quot;co1&quot;&gt;// Начать заряд переводом порта на вход&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;m &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; m &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;; m&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; NOP&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;  &lt;span class=&quot;co1&quot;&gt;// ПАУЗА для заряда.&lt;/span&gt;
&amp;nbsp;
        GODONE &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;                     &lt;span class=&quot;co1&quot;&gt;// Начинаем АЦ-преобразование&lt;/span&gt;
        GIE &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;                        &lt;span class=&quot;co1&quot;&gt;// Теперь прерывания можно разрешить&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;GODONE&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;continue&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  Измерение закончено, теперь разряжаем конденсатор, читаем результат&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  и формируем массив нажатых кнопок&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;Column.&lt;span class=&quot;me1&quot;&gt;pTris&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; ~Column.&lt;span class=&quot;me1&quot;&gt;cMask&lt;/span&gt;;
        &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;Column.&lt;span class=&quot;me1&quot;&gt;pPort&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; ~Column.&lt;span class=&quot;me1&quot;&gt;cMask&lt;/span&gt;; &lt;span class=&quot;co1&quot;&gt;// Разряжаем конденсатор переводом входа на&lt;/span&gt;
                                        &lt;span class=&quot;co1&quot;&gt;// выход и установкой &amp;quot;0&amp;quot; на нем&lt;/span&gt;
        a &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; ADRESH;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  Устанавливаем порог срабатывания, если он еще не установлен&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
&amp;nbsp;
        m &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; KBD.&lt;span class=&quot;me1&quot;&gt;Porogs&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;col&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;            &lt;span class=&quot;co1&quot;&gt;// Для сокращения кода копируем элемент&lt;/span&gt;
                                        &lt;span class=&quot;co1&quot;&gt;// массива в переменную&lt;/span&gt;
        i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;a &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; m&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; KBD.&lt;span class=&quot;me1&quot;&gt;cDataMask&lt;/span&gt;;   &lt;span class=&quot;co1&quot;&gt;// Сравниваем результат АЦП с порогом для&lt;/span&gt;
                                        &lt;span class=&quot;co1&quot;&gt;// данного канала. Если результат меньше&lt;/span&gt;
                                        &lt;span class=&quot;co1&quot;&gt;// порогового значения, значит,&lt;/span&gt;
                                        &lt;span class=&quot;co1&quot;&gt;// емкость на входе превышала допустим&lt;/span&gt;
                                        &lt;span class=&quot;co1&quot;&gt;// ое значение и кнопка считается нажатой&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;m&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;                         &lt;span class=&quot;co1&quot;&gt;// Если значение порога еще не установлено,&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;                               &lt;span class=&quot;co1&quot;&gt;// то формируем его как 88% от ADRESH&lt;/span&gt;
            m &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; a &lt;span class=&quot;sy1&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;;
            KBD.&lt;span class=&quot;me1&quot;&gt;Porogs&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;col&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; a &lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt; m;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  Установка нового значения кнопки с подавлением дребезга&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
        m  &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt;  KBD.&lt;span class=&quot;me1&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;KBD.&lt;span class=&quot;me1&quot;&gt;cDataPos&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;   &lt;span class=&quot;co1&quot;&gt;// Для сокращения кода работаем не с элементом&lt;/span&gt;
        k  &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt;  s_Changes&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;KBD.&lt;span class=&quot;me1&quot;&gt;cDataPos&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;  &lt;span class=&quot;co1&quot;&gt;// массива, а с фиксированной переменной&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;m &lt;span class=&quot;sy3&quot;&gt;^&lt;/span&gt; i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt; KBD.&lt;span class=&quot;me1&quot;&gt;cDataMask&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;                               &lt;span class=&quot;co1&quot;&gt;// Состояние кнопки изменилось:&lt;/span&gt;
            &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;k &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt; KBD.&lt;span class=&quot;me1&quot;&gt;cDataMask&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;   &lt;span class=&quot;co1&quot;&gt;//   Только что&lt;/span&gt;
                k |=  KBD.&lt;span class=&quot;me1&quot;&gt;cDataMask&lt;/span&gt;;    &lt;span class=&quot;co1&quot;&gt;//     Устанавливаем признак изменения бита&lt;/span&gt;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;                        &lt;span class=&quot;co1&quot;&gt;//   Не только что&lt;/span&gt;
            &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
                m &lt;span class=&quot;sy3&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt;  KBD.&lt;span class=&quot;me1&quot;&gt;cDataMask&lt;/span&gt;;    &lt;span class=&quot;co1&quot;&gt;//     устанавливаем новое значение кнопки&lt;/span&gt;
                s_bChanged &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;         &lt;span class=&quot;co1&quot;&gt;//     Формируем переменную для возврата&lt;/span&gt;
                k &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; ~KBD.&lt;span class=&quot;me1&quot;&gt;cDataMask&lt;/span&gt;;    &lt;span class=&quot;co1&quot;&gt;//     Сбрасываем признак изменения бита&lt;/span&gt;
            &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;                        &lt;span class=&quot;co1&quot;&gt;// Состояние кнопки не изменилось:&lt;/span&gt;
            k &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; ~KBD.&lt;span class=&quot;me1&quot;&gt;cDataMask&lt;/span&gt;;        &lt;span class=&quot;co1&quot;&gt;//   Сбрасываем признак изменения бита&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
        KBD.&lt;span class=&quot;me1&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;KBD.&lt;span class=&quot;me1&quot;&gt;cDataPos&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; m;     &lt;span class=&quot;co1&quot;&gt;// Восстанавливаем значения массивов из&lt;/span&gt;
        s_Changes&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;KBD.&lt;span class=&quot;me1&quot;&gt;cDataPos&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; k;    &lt;span class=&quot;co1&quot;&gt;// временных переменных&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//   Устанавливаем маску для следующей кнопки&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
        KBD.&lt;span class=&quot;me1&quot;&gt;cDataMask&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;KBD.&lt;span class=&quot;me1&quot;&gt;cDataMask&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
            KBD.&lt;span class=&quot;me1&quot;&gt;cDataMask&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x01&lt;/span&gt;;
            KBD.&lt;span class=&quot;me1&quot;&gt;cDataPos&lt;/span&gt;++;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;ROWS&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;row&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;pPort&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; ~ROWS&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;row&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;cMask&lt;/span&gt;;   &lt;span class=&quot;co1&quot;&gt;// Сбросить управляющий выход линии&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;return&lt;/span&gt; s_bChanged;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Обратим внимание на строку формирования паузы:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;m &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; m &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;; m&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; NOP&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
В данном случае пауза рассчитана для сопротивления в RC-цепочке, равное 100К, что обеспечивает заряд емкости примерно до Vdd/2. При установке резисторов других номиналов константу в цикле желательно (но не обязательно) пересчитать пропорционально, т.е. для 200К нужно будет считать до 6.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Клавиатура&quot; [47584-60238] --&gt;
&lt;h3&gt;&lt;a name=&quot;звук&quot; id=&quot;звук&quot;&gt;Звук&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Эта задача будет получать информацию о состоянии кнопок клавиатуры (через сообщение от &lt;strong&gt;Task_Keyboard&lt;/strong&gt;) и формировать переменные управления звуком для &amp;quot;Синтезатора&amp;quot;. При получении сообщения состояния кнопок копируются во внутренний массив для обработки, поскольку обработка предусматривает модификацию данных в этом массиве. После получения сообщения пробегаемся по всем переменным типа &lt;strong&gt;TSound&lt;/strong&gt;, содержащим информацию о том, на каком канале какая нота воспроизводится, с целью:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; прекращения воспроизведения уже не нажатых клавиш;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; удаления из списка нажатых клавиш уже воспроизводимых на данный момент;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; вычисления количества свободных каналов.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

После этого у нас есть переменная &lt;strong&gt;cFreeSounds&lt;/strong&gt;, показывающая сколько звуковых каналов свободно, и список еще не обрабатываемых клавиш в массиве &lt;strong&gt;Data&lt;/strong&gt;.
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;TSound      S&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;MAX_CHANNELS&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;    &lt;span class=&quot;co1&quot;&gt;// Переменные для формирования звука&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task_Sound &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    OST_MSG         msg;            &lt;span class=&quot;co1&quot;&gt;// Переменная для приема сообщения&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;   Data&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;KBD_SIZE&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;; &lt;span class=&quot;co1&quot;&gt;// Массив, куда будут скопированы состояния&lt;/span&gt;
                                    &lt;span class=&quot;co1&quot;&gt;// клавиш&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;   cMask;          &lt;span class=&quot;co1&quot;&gt;// Две вспомогательные переменны для побитового&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;   cPos;           &lt;span class=&quot;co1&quot;&gt;// индексирования кнопок в массиве Data&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;   cFreeSounds;    &lt;span class=&quot;co1&quot;&gt;// Переменная будет показывать, сколько свободных&lt;/span&gt;
                                    &lt;span class=&quot;co1&quot;&gt;// каналов (не воспроизводящих) есть на&lt;/span&gt;
                                    &lt;span class=&quot;co1&quot;&gt;// данный момент&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;   i, j;           &lt;span class=&quot;co1&quot;&gt;// Вспомогательные переменные&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//   Ждем изменения состояния кнопок.&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//   Копируем состояния кнопок в массив Data&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
        OS_Msg_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;msg_KBD, msg&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; i &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; KBD_SIZE; i&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; Data&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;msg&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  Из списка нажатых кнопок удаляем те, которые в данный момент&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  уже воспроизводятся. Одновременно считаем, сколько свободных каналов&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  имеется на данный момент.&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
        cFreeSounds &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; i &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; MAX_CHANNELS; i&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;      &lt;span class=&quot;co1&quot;&gt;// Пробегаемся по всем каналам&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
            &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;S&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;                  &lt;span class=&quot;co1&quot;&gt;// Если данный канал &amp;quot;молчит&amp;quot;, то&lt;/span&gt;
            &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;                                   &lt;span class=&quot;co1&quot;&gt;// увеличить счетчик свободных каналов&lt;/span&gt;
                cFreeSounds++;
                &lt;span class=&quot;kw1&quot;&gt;continue&lt;/span&gt;;
            &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
            j &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; S&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;                   &lt;span class=&quot;co1&quot;&gt;// Формируем адрес бита в массиве Data,&lt;/span&gt;
            cMask &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;j &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;               &lt;span class=&quot;co1&quot;&gt;// соответствующего текущему каналу&lt;/span&gt;
            cPos &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; j &lt;span class=&quot;sy1&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Data&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;cPos&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt; cMask&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;             &lt;span class=&quot;co1&quot;&gt;// Если кнопка все еще нажата, то&lt;/span&gt;
                Data&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;cPos&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; ~cMask;           &lt;span class=&quot;co1&quot;&gt;// Убираем ее из списка нажатых кнопок&lt;/span&gt;
            &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
                cFreeSounds++;                  &lt;span class=&quot;co1&quot;&gt;// Иначе прекращаем звук и увеличиваем&lt;/span&gt;
                S&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;                   &lt;span class=&quot;co1&quot;&gt;// счетчик свободных каналов.&lt;/span&gt;
            &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  На данный момент cFreeSound содержит число незадействованных каналов,&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  которые можно использовать для воспроизведения звуков для вновь&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//  нажатых клавиш&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
        cMask &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x01&lt;/span&gt;;   &lt;span class=&quot;co1&quot;&gt;// Поиск клавиш начинаем с первой&lt;/span&gt;
        cPos &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
        j &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;          &lt;span class=&quot;co1&quot;&gt;// Счетчик кнопок&lt;/span&gt;
        i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;          &lt;span class=&quot;co1&quot;&gt;// Счетчик каналов&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;j &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; KBD_KEYS&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; cFreeSounds&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
            &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Data&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;cPos&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt; cMask&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;     &lt;span class=&quot;co1&quot;&gt;// Клавиша нажата?&lt;/span&gt;
            &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;                           &lt;span class=&quot;co1&quot;&gt;// Да.&lt;/span&gt;
                &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;S&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; i++;   &lt;span class=&quot;co1&quot;&gt;// Ищем свободную ячейку&lt;/span&gt;
&amp;nbsp;
                                        &lt;span class=&quot;co1&quot;&gt;// Формируем звуковую переменную:&lt;/span&gt;
                S&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;F&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; Freq&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;j&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;       &lt;span class=&quot;co1&quot;&gt;// Устанавливаем частоту&lt;/span&gt;
                S&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;             &lt;span class=&quot;co1&quot;&gt;// Начальная фаза&lt;/span&gt;
                S&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; j &lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;       &lt;span class=&quot;co1&quot;&gt;// Запоминаем номер воспроизводимой клавиши&lt;/span&gt;
                cFreeSounds--;          &lt;span class=&quot;co1&quot;&gt;// Уменьшаем счетчик свободных каналов&lt;/span&gt;
            &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
            j++;                        &lt;span class=&quot;co1&quot;&gt;// Берем следующую кнопку для анализа&lt;/span&gt;
            cMask &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
            &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;cMask&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
            &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
                cMask &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x01&lt;/span&gt;;
                cPos++;
            &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Звук&quot; [60239-66778] --&gt;
&lt;h3&gt;&lt;a name=&quot;синтезатор1&quot; id=&quot;синтезатор1&quot;&gt;Синтезатор&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Как мы уже решили раньше, подпрограмма синтезатора звука будет помещена внутрь прерывания по TMR2. Таймер 2 у нас настроен и для отсчета тактов модуля ШИМ, и для генерации прерываний. ШИМ у нас выбран максимально возможной частоты для 20 МГц и 8-разрядного разрешения, т.е. 78КГц. Теперь нам нужно выбрать постделитель для TMR2 такой, чтобы обеспечить частоту семплирования 20КГц. Очевидно, что ближайшим значением будет 4 (при этом мы получим частоту 19500). Итак, каждые 51.2 мкс вызывается прерывание, в котором генерируется звук, а именно - скважность импульсов ШИМ-сигнала.
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; interrupt isr &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt;  &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt;    &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;    prs;        &lt;span class=&quot;co1&quot;&gt;// Предделитель для вызова OS_Timer&lt;/span&gt;
            &lt;span class=&quot;kw4&quot;&gt;signed&lt;/span&gt;      &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;     temp_dac;   &lt;span class=&quot;co1&quot;&gt;// Сумма мгновенного значения&lt;/span&gt;
                                            &lt;span class=&quot;co1&quot;&gt;// сигнала для всех каналов&lt;/span&gt;
            &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt;    &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;    m_cDAC;     &lt;span class=&quot;co1&quot;&gt;// Переменная для вывода через ШИМ&lt;/span&gt;
&amp;nbsp;
&amp;nbsp;
    TMR2IF &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
    temp_dac &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//  Формируем мгновенное значение суммы всех каналов&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
    SOUND&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    SOUND&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    SOUND&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    SOUND&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    SOUND&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    SOUND&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    SOUND&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    SOUND&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    temp_dac &lt;span class=&quot;sy1&quot;&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;;     &lt;span class=&quot;co1&quot;&gt;// Т.к. 8 каналов, то сумму делим на 8.&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//  Выводим полученное значение через ШИМ&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
    m_cDAC  &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;temp_dac&lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x80&lt;/span&gt;;
    m_cDAC &lt;span class=&quot;sy1&quot;&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;;
    CCP_bit1 &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
    CCP_bit0 &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;temp_dac &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; CCP_bit1 &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;temp_dac &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; CCP_bit0 &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; ;
    CCPR1L &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; m_cDAC;
&amp;nbsp;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Обратим внимание на вызов макросов SOUND(x). Этот макрос написан просто для удобства добавления/удаления каналов в зависимости от требований к качеству звука и тактовой частоты контроллера (Например, понизив тактовую частоту в два раза, мы за 50 мкс будем успевать обработать только 4 канала; или, снизив частоту семплирования до 10 КГц, мы сможем обработать 16 каналов). Сам макрос выглядит так:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define SOUND(x)                                        \&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;S&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;x&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;                                     \
        temp_dac &lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; Sample&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;S&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;x&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x3F&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;; \
        &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;S&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;x&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;S&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;x&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;     \
        &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;S&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;x&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;S&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;x&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;     \
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;CARRY&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;S&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;x&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;            \
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
После проверки активности канала по полю &lt;strong&gt;cKey&lt;/strong&gt; мы из массива, где хранится оцифрованный период для конкретного музыкального инструмента, в соответствии с текущей фазой сигнала (поле &lt;strong&gt;f&lt;/strong&gt;) выбираем нужное значение и прибавляем его к общей сумме &lt;strong&gt;temp_dac&lt;/strong&gt;. После этого сдвигаем фазу на шаг, зависящий от частоты ноты, которая проигрывается на данном канале.
&lt;/p&gt;

&lt;p&gt;
Теперь в прерывание осталось добавить обработку системных таймеров. Выбираем интервал для таймера равный 10 мс, или двумстам вызовам прерывания.
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//  1 раз в 200 вызовов (200 * 50мкс = 10мс) вызываем системный таймер&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;--&lt;/span&gt;prs&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        OS_Timer&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;     &lt;span class=&quot;co1&quot;&gt;// Обработка системных таймеров&lt;/span&gt;
        prs &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;200&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Синтезатор&quot; [66779-71690] --&gt;
&lt;h3&gt;&lt;a name=&quot;main&quot; id=&quot;main&quot;&gt;main()&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Здесь будут проинициализированы периферия и операционная система, а также будут созданы задачи и запущен планировщик. Все задачи имеют одинаковый высший (нулевой) приоритет.
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; main &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//  Инициализация периферии&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
    Init&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//  Инициализация системы&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
    OS_Init&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//  Создание задач (все задачи имеют одинаковый высший приоритет)&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
    OS_Task_Create&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;, Task_Sound&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Task_Create&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;, Task_Button&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Task_Create&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;, Task_Keyboard&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//  Разрешаем прерывания и запускаем планировщик&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
    OS_EI&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Run&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;main()&quot; [71691-73258] --&gt;
&lt;h3&gt;&lt;a name=&quot;init&quot; id=&quot;init&quot;&gt;Init()&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Весь текст я здесь приводить не буду (его можно посмотреть в исходных текстах, прилагаемых к статье), т.к. из-за того, что эта функция предусматривает работу на 4-х разных контроллерах (16F886, 16F887, 16F690 и 16F88), то код ее довольно громоздкий из-за наличия условных директив #ifdef…#endif. 
&lt;/p&gt;

&lt;p&gt;
Скажу только, что в этой функции производится инициализация: 
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; потов ввода/вывода; &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; АЦП; &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; таймеров; &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; модуля ШИМ;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; прерываний.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Init()&quot; [73259-74018] --&gt;
&lt;h3&gt;&lt;a name=&quot;конфигурация_osa&quot; id=&quot;конфигурация_osa&quot;&gt;Конфигурация OSA&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Для конфигурирования работы операционной системы в нашем проекте воспользуемся утилитой OSAcfg_Tool.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;выбираем_папку_где_располагается_наш_проект&quot; id=&quot;выбираем_папку_где_располагается_наш_проект&quot;&gt;1. Выбираем папку, где располагается наш проект&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Для этого в самом верху окна справа нажимаем кнопку &lt;strong&gt;Browse&lt;/strong&gt;. Там выбираем путь к файлу OSAcfg.h - путь к нашему проекту (&amp;quot;C:\TEST\PICKIT2\PIANO&amp;quot;). Нажимаем OK. Если файл еще не создан, то программа спросит у Вас, действительно ли Вы хотите создать этот файл. Смело отвечаем &amp;quot;Yes&amp;quot; и идем дальше. 
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;выбираем_имя_проекта&quot; id=&quot;выбираем_имя_проекта&quot;&gt;2. Выбираем имя проекта&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

В поле &lt;strong&gt;Name&lt;/strong&gt; можно ввести имя проекта. Этот пункт необязателен, а имя вводится исключительно для наглядности, чтобы не путаться потом, какой файл от какого проекта. Мы введем в эту строку &amp;quot;ПИАНИНО&amp;quot;.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;выбираем_платформу&quot; id=&quot;выбираем_платформу&quot;&gt;3. Выбираем платформу&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Также необязательный пункт. Служит только для того, чтобы пользователь при конфигурировании файла в реальном времени наблюдал предполагаемый расход оперативной памяти операционной системой. Для успокоения выберем платформу: 14-бит (PIC12, PIC16)(ht-picc). Теперь при изменении настроек мы автоматически в рамке &lt;strong&gt;RAM statistic&lt;/strong&gt; будем видеть, сколько байтов в каком банке памяти израсходовано.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;конфигурируем_наш_проект&quot; id=&quot;конфигурируем_наш_проект&quot;&gt;4. Конфигурируем наш проект&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Учитывая, что мы решили не использовать приоритеты (т.е. все задачи сделать равноприоритетными), можно установить галочку напротив пункта &lt;strong&gt;Disable priority&lt;/strong&gt;. Это сократит размер кода ядра операционной системы и ускорит работу планировщика.
&lt;/p&gt;

&lt;p&gt;
Далее, нам обязательно нужно выбрать количество задач ОС, которые будут работать одновременно. В нашем случае - 3 (по количеству задач, создаваемых сервисом &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Task_Create; как уже было сказано раньше, 4-я задача у нас не является задачей ОС и располагается в обработчике прерывания). 
&lt;/p&gt;

&lt;p&gt;
Учитывая, что сама программа использует много переменных, есть смысл все системные переменные затолкать в какой-нибудь верхний банк памяти, например &lt;strong&gt;bank2&lt;/strong&gt; (в поле &lt;strong&gt;OSA variables bank&lt;/strong&gt;).
&lt;/p&gt;

&lt;p&gt;
Теперь нам нужно сказать системе, что нам требуются два статических таймера для работы. В поле &lt;strong&gt;Static timers&lt;/strong&gt; устанавливаем 2 и в таблице ниже вводим имена идентификаторов статических таймеров: &lt;strong&gt;ST_KEYBOARD&lt;/strong&gt; и &lt;strong&gt;ST_BUTTON&lt;/strong&gt;. Кроме того, учитывая, что нам не потребуются задержки длиннее 256 системных тиков, установим тип статического таймера &lt;strong&gt;char&lt;/strong&gt;.
&lt;/p&gt;

&lt;p&gt;
И последнее: для ускорения обработки сервиса &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer установим галочку напротив пункта &lt;strong&gt;Use in-line &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer()&lt;/strong&gt;.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;сохраняем_и_выходим&quot; id=&quot;сохраняем_и_выходим&quot;&gt;5. Сохраняем и выходим&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Жмем на кнопку &lt;strong&gt;Save&lt;/strong&gt;, чтобы сохранить отредактированный файл конфигурации, и выходим из программы нажатием на кнопку &lt;strong&gt;Exit&lt;/strong&gt;. Теперь, заглянув в созданный нами файл, мы увидим следующее:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;// This file was generated by OSAcfg_Tool utility.&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;// Do not modify it to prevent data loss on next editing.&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;// PROJECT NAME: ПИАНИНО&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;// PLATFORM:     HT-PICC 14-bit&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co2&quot;&gt;#ifndef _OSACFG_H&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define _OSACFG_H&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;// SYSTEM&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co2&quot;&gt;#define OS_TASKS               3  // Number of tasks that can be active at one time&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define OS_DISABLE_PRIORITY       //&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;// ENABLE CONSTANTS&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co2&quot;&gt;#define OS_USE_INLINE_TIMER        // Make OS_Timer service as in-line function&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;// BANKS&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co2&quot;&gt;#define OS_BANK_OS             2  // RAM bank to allocate all system variables&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;// TYPES&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co2&quot;&gt;#define OS_STIMER_SIZE         1  // Size of static timers (1, 2 or 4)&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;// STIMERS&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co2&quot;&gt;#define OS_STIMERS             2  // Number of static timers&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw2&quot;&gt;enum&lt;/span&gt; OSA_STIMERS_ENUM
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    ST_KEYBOARD,          &lt;span class=&quot;co1&quot;&gt;// Для формирования задержек в Task_Keyboard&lt;/span&gt;
    ST_BUTTON             &lt;span class=&quot;co1&quot;&gt;// Для формирования задержек в Task_Button&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;co2&quot;&gt;#endif&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Конфигурация OSA&quot; [74019-80383] --&gt;
&lt;h2&gt;&lt;a name=&quot;прошивка_контроллера&quot; id=&quot;прошивка_контроллера&quot;&gt;Прошивка контроллера&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Прошивка контроллера&quot; [80384-80436] --&gt;
&lt;h3&gt;&lt;a name=&quot;сборка_проекта&quot; id=&quot;сборка_проекта&quot;&gt;Сборка проекта&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Для работы с проектом нам нужно иметь установленную интегрированную среду &lt;a href=&quot;http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&amp;amp;nodeId=1406&amp;amp;dDocName=en019469&amp;amp;part=SW007002&quot; class=&quot;urlextern&quot; title=&quot;http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&amp;amp;nodeId=1406&amp;amp;dDocName=en019469&amp;amp;part=SW007002&quot;  rel=&quot;nofollow&quot;&gt;MPLAB IDE&lt;/a&gt;, установленный компилятор &lt;a href=&quot;http://www.htsoft.com/microchip/products/compilers/picccompiler.php&quot; class=&quot;urlextern&quot; title=&quot;http://www.htsoft.com/microchip/products/compilers/picccompiler.php&quot;  rel=&quot;nofollow&quot;&gt;HI-TECH PICC STD&lt;/a&gt; (PRO-версия не подойдет).
&lt;/p&gt;

&lt;p&gt;
Скачиваем, если еще не скачали, &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/lib/exe/fetch.php?media=http%3A%2F%2Fwiki.pic24.ru%2Flib%2Fexe%2Ffetch.php%2Fosa%2Fhistory%2Fosa_90402.zip&quot; class=&quot;media mediafile mf_zip&quot; title=&quot;http://wiki.pic24.ru/lib/exe/fetch.php/osa/history/osa_90402.zip&quot;&gt;файлы операционной системы OSA&lt;/a&gt;&lt;/strong&gt;, распаковываем этот архив на диск C: (должна получиться папка C:\OSA).
&lt;/p&gt;

&lt;p&gt;
Распаковываем файл &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/lib/exe/fetch.php?media=http%3A%2F%2Fwiki.pic24.ru%2Flib%2Fexe%2Ffetch.php%2Fosa%2Fpiano.rar&quot; class=&quot;media mediafile mf_rar&quot; title=&quot;http://wiki.pic24.ru/lib/exe/fetch.php/osa/piano.rar&quot;&gt;piano.rar&lt;/a&gt;&lt;/strong&gt; в папку C:\TEST\PICKIT2. При этом внутри создастся папка &lt;strong&gt;PIANO&lt;/strong&gt;. В MPLAB IDE открываем проект, в названии которого присутствует номер контроллера, который Вы собираетесь использовать: 886, 887, 690 или 88. Например, для демо-платы на базе 16F887 нам нужно открыть файл pk2_piano_887.mcp.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Примечание.&lt;/strong&gt;&lt;em&gt; При распаковке в другую папку, отличную от C:\TEST\PICKIT2\PIANO, нужно будет через меню Project\Build options…\Project в закладке Directories в списке include-путей заменить путь к файлам проекта на тот, куда Вы распаковали файлы из архива.&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
Выполняем сборку нажатием &lt;strong&gt;Ctrl+F10&lt;/strong&gt;.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Сборка проекта&quot; [80437-82270] --&gt;
&lt;h3&gt;&lt;a name=&quot;прошивка&quot; id=&quot;прошивка&quot;&gt;Прошивка&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Здесь все просто: 
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; подключаем программатор;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; в меню &amp;quot;Programmer\Select&amp;quot; programmer выбираем PicKit2;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; В настройках &amp;quot;Programmer→Settings&amp;quot; выбираем &amp;lt;3-State on &amp;lt;Release from Reset&amp;gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; запускаем программирование &amp;quot;Programmer\Program&amp;quot;;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; освобождаем вывод MCLR &amp;quot;Programmer\Release from reset&amp;quot;.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Вот и все!
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Прошивка&quot; [82271-82730] --&gt;
&lt;h2&gt;&lt;a name=&quot;схемы_подключения_клавиатурной_матрицы&quot; id=&quot;схемы_подключения_клавиатурной_матрицы&quot;&gt;Схемы подключения клавиатурной матрицы&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Здесь я просто приведу к каким выводам подключается матрица, чтобы работала наша программа. Рассмотрены контроллеры PIC16F88, PIC16F690, PIC16F886, PIC16F887.
&lt;/p&gt;

&lt;p&gt;
&lt;em&gt;&lt;strong&gt;Важное примечание:&lt;/strong&gt; на выводах, используемых для чтения сенсорных датчиков (col1, col2 и т.д. - аналоговвые входы) не должно висеть ничего, кроме самих датчиков, т.к. любой внешний элемент будет вносить свои емкость и сопротивление.&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/pk2_osa_piano_scheme_matrix.png?id=osa%3Aarticles%3Apk2_osa_piano&quot; class=&quot;media&quot; title=&quot;osa:articles:pk2_osa_piano_scheme_matrix.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/pk2_osa_piano_scheme_matrix.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Аналоговые входы контроллера дополнительно защищены от статики согласно документа от Microchip&lt;a href=&quot;http://ww1.microchip.com/downloads/en/AppNotes/01102a.pdf&quot; class=&quot;urlextern&quot; title=&quot;http://ww1.microchip.com/downloads/en/AppNotes/01102a.pdf&quot;  rel=&quot;nofollow&quot;&gt;Layout and Physical Design Guidelines for Capacitive Sensing (PDF)&lt;/a&gt;. Хоть контроллер и имеет встроенную защиту, тем не менее, во-первых, в некоторых случаях ее может оказаться недостаточно, а во-вторых, защиту имеют не все выводы (например, RA4 ее не имеет).
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/pk2_osa_piano_scheme690.png?id=osa%3Aarticles%3Apk2_osa_piano&quot; class=&quot;media&quot; title=&quot;osa:articles:pk2_osa_piano_scheme690.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/pk2_osa_piano_scheme690.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;br/&gt;
 
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/pk2_osa_piano_scheme88.png?id=osa%3Aarticles%3Apk2_osa_piano&quot; class=&quot;media&quot; title=&quot;osa:articles:pk2_osa_piano_scheme88.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/pk2_osa_piano_scheme88.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;br/&gt;
 
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/pk2_osa_piano_scheme886.png?id=osa%3Aarticles%3Apk2_osa_piano&quot; class=&quot;media&quot; title=&quot;osa:articles:pk2_osa_piano_scheme886.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/pk2_osa_piano_scheme886.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;br/&gt;
 
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/pk2_osa_piano_scheme887.png?id=osa%3Aarticles%3Apk2_osa_piano&quot; class=&quot;media&quot; title=&quot;osa:articles:pk2_osa_piano_scheme887.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/pk2_osa_piano_scheme887.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;br/&gt;
 
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Схемы подключения клавиатурной матрицы&quot; [82731-84312] --&gt;
&lt;h2&gt;&lt;a name=&quot;заключение&quot; id=&quot;заключение&quot;&gt;Заключение&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Я уверен, что если у читателя хватило терпения дочитать досюда, то у него без труда хватит терпения собрать матрицу и клавиатуру и, подключив все это дело к демо-плате из набора PicKit2, насладиться игрой на собственноручно сделанном музыкальном инструменте.
&lt;/p&gt;

&lt;p&gt;
Итак, в статье были рассмотрены две интересные для многих начинающих программистов темы: обработка кнопок и генерация звука. Причем рассматривались не просто кнопки, а сенсорные кнопки, что расширяет область их применения (они могут работать и в сырости, и в пыли); и мы разобрались с генерацией не просто звука, а многоканального звука. Уверен, что знания, полученные при прочтении данного пособия, а возможно и какие-то программные наработки, расширят Ваши возможности и позволят свои программы украшать интересными интерфейсными решениями.
&lt;/p&gt;

&lt;p&gt;
Зачем мы воспользовались RTOS? 
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Во-первых, конечно же, для того, чтобы не думать о том, как бы нам самим реализовать распараллеливание процессов. &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Во-вторых, четко разбили все на задачи, каждая из которых получилась простой наглядной. Кроме того, любая из задач может быть с легкостью модифицирована под конкретные нужды.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Наконец, сделали наши задачи независимыми, что позволит внедрять их в другие программы, или расширить возможности этой (например, можно дописать задачу, отсылающие задаче &lt;strong&gt;Task_Sound&lt;/strong&gt; сообщения, чтобы просто проигрывать любую зашитую в контроллер мелодию).&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Наша программа написана с тем учетом, чтобы она могла быть легко модифицирована под любые пожелания:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; хотим кнопки перевесить на другие выводы контроллера - на здоровье: правим массивы &lt;strong&gt;ROWS&lt;/strong&gt; и &lt;strong&gt;COLUMNS&lt;/strong&gt; в файле &lt;strong&gt;const.h&lt;/strong&gt;;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; хотим больше кнопок - пожалуйста, переопределяем константы &lt;strong&gt;KBD_ROWS&lt;/strong&gt; и &lt;strong&gt;KBD_COLUMNS&lt;/strong&gt; и добавляем данные о новых выводах в массивы &lt;strong&gt;ROWS&lt;/strong&gt; и &lt;strong&gt;COLUMNS&lt;/strong&gt;;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; хотим больше каналов - увеличиваем константу &lt;strong&gt;MAX_CHANNELS&lt;/strong&gt; и снижаем частоту семплирования;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; хотим новых инструментов - правим массив &lt;strong&gt;SAMPLES&lt;/strong&gt; в файле sinus.c.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; и т.д. &lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Все ограничивается только фантазией! 
&lt;/p&gt;

&lt;p&gt;
Удачи!
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Заключение&quot; [84313-87886] --&gt;
&lt;h2&gt;&lt;a name=&quot;ссылки&quot; id=&quot;ссылки&quot;&gt;Ссылки&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://elm-chan.org/works/mxb/report.html&quot; class=&quot;urlextern&quot; title=&quot;http://elm-chan.org/works/mxb/report.html&quot;  rel=&quot;nofollow&quot;&gt;музыкальная шкатулка от Чана (на attiny45)&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://pickit2.ru/doku.php/%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D1%8B:%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F.mtouch.%D0%BD%D0%B0.%D1%81%D0%B8&quot; class=&quot;urlextern&quot; title=&quot;http://pickit2.ru/doku.php/%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D1%8B:%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F.mtouch.%D0%BD%D0%B0.%D1%81%D0%B8&quot;  rel=&quot;nofollow&quot;&gt;сенсорные кнопки&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://gamma.spb.ru/articles.php?i=84&quot; class=&quot;urlextern&quot; title=&quot;http://gamma.spb.ru/articles.php?i=84&quot;  rel=&quot;nofollow&quot;&gt;еще про сенсорные кнопки&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Поделки по данной статье:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://b612.h16.ru/pianino.htm&quot; class=&quot;urlextern&quot; title=&quot;http://b612.h16.ru/pianino.htm&quot;  rel=&quot;nofollow&quot;&gt;http://b612.h16.ru/pianino.htm&lt;/a&gt; - пианино на PIC16F690 с выводом звука на динамик через усилитель. Прилагается схема, печатная плата, файлы проекта для Proteus, а также небольшой файл с демо (mp3)&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;br/&gt;
 
&lt;br/&gt;
 
&lt;br/&gt;
 
&lt;/p&gt;

&lt;p&gt;
Виктор Тимофеев, апрель 2009&lt;br/&gt;
 
&lt;a href=&quot;mailto:&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot; class=&quot;mail JSnocheck&quot; title=&quot;&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot;&gt;osa@pic24.ru&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Ссылки&quot; [87887-] --&gt;</description>
    </item>
    <item rdf:about="http://www.pic24.ru/doku.php/osa/articles/rtos_misbeliefs?rev=1305360719">
        <dc:format>text/html</dc:format>
        <dc:date>2011-05-14T12:11:59+03:00</dc:date>
        <title>RTOS: распространенные заблуждения</title>
        <link>http://www.pic24.ru/doku.php/osa/articles/rtos_misbeliefs?rev=1305360719</link>
        <description>


&lt;h1&gt;&lt;a name=&quot;rtosраспространенные_заблуждения&quot; id=&quot;rtosраспространенные_заблуждения&quot;&gt;RTOS: распространенные заблуждения&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;

&lt;span class=&quot;important&quot;&gt;Шесть распространенных заблуждений о применении RTOS в малоресурсных МК&lt;/span&gt;
&lt;/p&gt;

&lt;p&gt;
Среди опасений, являющихся препятствием к освоению RTOS, встречаются и малообоснованные, которые, тем не менее, зачастую являются решающим фактором выбора не в пользу RTOS. С другой стороны существуют и противоположные заблуждения, сильно преувеличивающие возможности RTOS и выгоды ее применения. Некоторые довольно часто встречающиеся заблуждения довольно легко развеиваются.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;RTOS: распространенные заблуждения&quot; [1-901] --&gt;
&lt;h2&gt;&lt;a name=&quot;заблуждение_1&quot; id=&quot;заблуждение_1&quot;&gt;Заблуждение 1&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

&lt;span class=&quot;important&quot;&gt;«Моя программа очень простая. Зачем там RTOS?»&lt;/span&gt;
&lt;/p&gt;

&lt;p&gt;
Здесь есть два аспекта. Почти любое устройство решает не одну задачу, а несколько (пускай даже опрос кнопки и мигание светодиодом). Поэтому подобие планировщика и средств разделения ресурсов между ними будет присутствовать. Так что то, что программа работает без управления какой-то конкретной ОС, вовсе не означает, что она не содержит подпрограмм или просто фрагментов кода, по сути выполняющих ее функции. Просто они получаются разбросанными по всему коду так, что их трудно однозначно выделить из программы в целом.
&lt;/p&gt;

&lt;p&gt;
Кроме того, программа, созданная для коммерческого устройства, не всегда может оставаться простой из-за конкуренции на рынке аналогичных устройств. В качестве примера можно рассмотреть эволюцию любого бытового прибора, например, электрического чайника. 
&lt;/p&gt;

&lt;p&gt;
Первые электрические чайники, начавшие вытеснять традиционные «кастрюли с носиком», представляли из себя те же кастрюли, но с нагревательным элементом внутри и проводом снаружи для включения в розетку. Такие чайники было неудобно наполнять водой и мыть из-за намертво вмонтированного провода, поэтому со временем чайники начали снабжать разъемом и отсоединяемым проводом. Часто нагревательный элемент выходил из строя, когда про включенный чайник забывали, тогда чайники стали снабжать кнопкой включения и автоматом отключения. Иногда провод чайника вынимали из розетки, чтобы воспользоваться другим прибором, а потом забывали его вставить обратно - чайники начали снабжаться световым индикатором. Далее – сделали изолированный нагревательный элемент, индикатор уровня воды, контактную подставку, защитный механизм на крышку (чтобы не открывалась, когда внутри кипяток) и т.д. и т.п. 
&lt;/p&gt;

&lt;p&gt;
И сейчас простой обыватель не купит чайник, не имеющий большей части перечисленных удобств. То же самое с любым другим устройством. Сегодня это обычный датчик влажности, а завтра к нему придется прикрутить цветной дисплей и снабдить его голосовыми сообщениями. Таким образом, вместо одной задачи устройству придется выполнять пять или десять. И здесь RTOS будет хорошим помощником.
&lt;/p&gt;

&lt;p&gt;
Разумеется, приведенные доводы не означают, что RTOS следует применять везде и всюду. Остается класс задач, где она помешает, но это уже за рамками постановки данного вопроса.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Заблуждение 1&quot; [902-5093] --&gt;
&lt;h2&gt;&lt;a name=&quot;заблуждение_2&quot; id=&quot;заблуждение_2&quot;&gt;Заблуждение 2&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

&lt;span class=&quot;important&quot;&gt;«RTOS – черный ящик, а я хочу контролировать все»&lt;/span&gt;
&lt;/p&gt;

&lt;p&gt;
Часто программисты испытывают страх перед тем, что будет потерян абсолютный контроль над генерируемым кодом. Подобные страхи возникают на первых этапах применения языка высокого уровня после длительного использования ассемблера. Однако со временем приходит понимание цены полного абсолютного контроля, и эффективности приношения в жертву малого во имя выигрыша в большем. В отношении RTOS все то же самое.
&lt;/p&gt;

&lt;p&gt;
Когда кто-то задается вопросом «а как я буду все контролировать?», хорошо бы еще ответить на вопрос «действительно ли это нужно в конкретной программе?». Оглянитесь вокруг: нас окружают десятки, сотни и тысячи разнообразных устройств. Попробуйте мысленно спроектировать программу для любого устройства дома: будильник, пульт д/у, музыкальный звонок, холодильник и т.д. Выйдите на улицу: светодиодные вывески над магазинчиками, сигнализации почти в каждом автомобиле, домофоны. Зайдите в магазин: кассовые аппараты, система освещения, турникет с магнитной рамкой. А теперь ответьте себе на вопрос: какие из узлов программы, которую бы вы написали для управления этими устройствами, требуют абсолютного контроля генерируемого кода со стороны программиста? 
&lt;/p&gt;

&lt;p&gt;
Вариантов будет не так много, как кажется на первый взгляд. Тем не менее, существует круг задач, при решении которых применять RTOS неразумно. Поэтому примерять RTOS на любой проект не стоит, но в большинстве приложений имеет смысл сперва выделить ту часть будущей программы, которая требует постоянного контроля, а затем оценить, есть ли возможность ее решить аппаратными средствами выбранной линейки микроконтроллеров. И только потом делать вывод о нерентабельности применения RTOS в конкретном проекте. 
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Заблуждение 2&quot; [5094-8276] --&gt;
&lt;h2&gt;&lt;a name=&quot;заблуждение_3&quot; id=&quot;заблуждение_3&quot;&gt;Заблуждение 3&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

&lt;span class=&quot;important&quot;&gt;«RTOS отнимает слишком много ресурсов»&lt;/span&gt;
&lt;/p&gt;

&lt;p&gt;
Несомненно, RTOS под свои нужды использует часть ресурсов микроконтроллера. Однако не следует преувеличивать предполагаемые потери. Для многих важным аргументом не в пользу использования RTOS в своих проектах является опасение, что из-за лишнего кода в виде ОС, программа не уместится в выбранный контроллер. Помимо очевидных аргументов необоснованности этих опасений (широкий выбор микроконтроллеров, незначительная разница цен), есть менее заметные. 
&lt;/p&gt;

&lt;p&gt;
Программист, считающий, что пишет оптимальные и эффективные программы, для которых можно использовать более дешевый контроллер с меньшим объемом памяти, и думающий, что добавление такой ненужной сущности, как RTOS, приведет к нехватке нескольких байт, должен задать себе вопрос: каков его личный профессиональный уровень? 
&lt;/p&gt;

&lt;p&gt;
Если один программист сумел в выбранный контроллер втолкать программу без RTOS, то может найтись более квалифицированный программист, который сделает то же самое, но с RTOS. И с этой стороны миф о нехватке ресурсов оборачивается в правду о нехватке квалификации. С другой стороны, на одного очень квалифицированного программиста придется десять менее квалифицированных, которым подобная оптимизация даже без RTOS окажется не по силам, т.е. им в любом случае понадобится микроконтроллер с большими ресурсами. 
&lt;/p&gt;

&lt;p&gt;
Кроме того, есть какая-то опрометчивость в том, чтобы еще на этапе проектирования предположить, что программа будет использовать все ресурсы под завязку. В самом деле, нельзя же быть уверенным в том, что любая программа потребует «почти 1кб» программной памяти, или «почти 2кб», или «почти 4кб», или «почти 8кб» и т.д.
&lt;/p&gt;

&lt;p&gt;
В результате получается, что проблема нехватки ресурсов при использовании RTOS касается немногих программистов, да и то – далеко не на всех проектах. Поэтому проблема надуманна, и ее вряд ли можно всерьез рассматривать как аргумент в пользу отказа от RTOS.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Заблуждение 3&quot; [8277-11769] --&gt;
&lt;h2&gt;&lt;a name=&quot;заблуждение_4&quot; id=&quot;заблуждение_4&quot;&gt;Заблуждение 4&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

&lt;span class=&quot;important&quot;&gt;«Код написан дядей. Он ошибется, а я – расхлебывай!»&lt;/span&gt;
&lt;/p&gt;

&lt;p&gt;
Некоторые мотивируют нежелание использования RTOS опасениями, что ее код написан другими людьми, и если вдруг в нем обнаружится ошибка, то исправить ее самостоятельно окажется очень затруднительно. Опасение, в принципе, обоснованное. RTOS может содержать ошибки, как и компилятор (достаточно посмотреть список обновлений) или даже сам микроконтроллер (можно заглянуть в документы errata). Но все же основания для таких опасений сильно преувеличены. 
&lt;/p&gt;

&lt;p&gt;
Во-первых, создание таких комплексов программ, как RTOS, требуют от программиста не только знания принципов работы операционных систем и особенностей платформы, на которую ориентирована RTOS, но также высокой профессиональной подготовки и глубокого знания целевых компиляторов. Другими словами написанием RTOS занимаются программисты высокой квалификации, и вероятность столкнуться с проявлением допущенной ими ошибки намного ниже, чем с проявлением собственной.
&lt;/p&gt;

&lt;p&gt;
Во-вторых, в отладке RTOS, помимо ее авторов, принимают участие еще и пользователи. Сама RTOS эксплуатируется в самых различных условиях разными людьми с разным подходом к программированию. Это создает благоприятные условия для эффективного тестирования. Часть пользователей сообщают авторам об обнаруженных ошибках, зачастую сопровождая сообщения подробным описанием условий проявления, а иногда даже исходными текстами, где ошибка проявляется. Это значительно ускоряет и упрощает процесс отладки и исправления ошибок. Не каждый программист может рассчитывать на такое массивное тестирование своей программы: результаты месячного тестирования программы одним человеком не идут ни в какое сравнение с результатами многолетнего тестирования сотнями программистов.
&lt;/p&gt;

&lt;p&gt;
Наконец, в-третьих, нужно учитывать возраст самой RTOS. Поведение программы, которая применяется многими людьми в течение нескольких лет намного более предсказуемо, чем поведение только что написанной программы.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Заблуждение 4&quot; [11770-15368] --&gt;
&lt;h2&gt;&lt;a name=&quot;заблуждение_5&quot; id=&quot;заблуждение_5&quot;&gt;Заблуждение 5&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

&lt;span class=&quot;important&quot;&gt;«Если используешь RTOS, то контроллер и язык программирования можно изучить только поверхностно»&lt;/span&gt;
&lt;/p&gt;

&lt;p&gt;
Это часто совершаемая начинающими ошибка. Некоторыми начинающими программистами RTOS воспринимается как инструмент, избавляющий от необходимости хорошо знать язык программирования и вникать в тонкости архитектуры применяемого микроконтроллера. Это заблуждение приводит к созданию ненадежных и трудно сопровождаемых программ, а такие результаты отбивают охоту не только использовать RTOS, но и вообще заниматься программированием микроконтроллеров.
&lt;/p&gt;

&lt;p&gt;
А на самом деле программа-то все равно пишется на конкретном языке программирования, по его правилам и с его ограничениями; она все равно пишется под конкретный целевой микроконтроллер, имеющий свою архитектуру и свои особенности. Более того, при использовании RTOS следует как раз более тщательно изучать как архитектуру микроконтроллера, так и тонкости языка программирования. Особое внимание придется уделять таким приемам, как обеспечение атомарного доступа к глобальным переменным и регистрам, знать, как конкретный компилятор организует хранение локальных переменных и пр. Другими словами следует уделять внимание таким аспектам, появление которых при программировании без использования RTOS встречается гораздо реже.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Заблуждение 5&quot; [15369-17753] --&gt;
&lt;h2&gt;&lt;a name=&quot;заблуждение_6&quot; id=&quot;заблуждение_6&quot;&gt;Заблуждение 6&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

&lt;span class=&quot;important&quot;&gt;RTOS делает контроллер мощнее&lt;/span&gt;
&lt;/p&gt;

&lt;p&gt;
Как ни странно, распространенное заблуждение о том, что если мощности контроллера не хватает для полноценного функционирования программы, то добавление в программу RTOS может решить проблему, - присуще даже опытным программистам. Нередко можно услышать от них довод: «Зачем тут RTOS? Я вот писал такое, да растакое на ассемблере – и справился без всяких RTOS!» Довод довольно нелепый и скорее говорит за невладение вопросом, чем за умение обходиться ассемблером (или Си).
&lt;/p&gt;

&lt;p&gt;
Истоки этого заблуждения, скорее всего, в рассуждениях малоопытных программистов, большинство программ которых написаны крайне неэффективно и неоптимально. При попытке перевести программу под управление какой-нибудь RTOS, имеющей в своем составе намного более эффективные решения, они обнаруживали, что программа вдруг стала быстрее и компактнее, чем была. Это достижение тут же доносится до широких масс и принимается на вооружение всеми, кто не имел дела с RTOS ранее: и опытными и неопытными.
&lt;/p&gt;

&lt;p&gt;
На самом же деле надо понимать, что ни памяти, ни скорости микроконтроллеру RTOS не добавит, а даже наоборот – часть этих ресурсов заберет под свои нужды. Поэтому, если микроконтроллер не справляется с возложенными на него задачами, в большинстве случаев это означает, что нужно применить другой, более мощный, контроллер.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Заблуждение 6&quot; [17754-20189] --&gt;
&lt;h2&gt;&lt;a name=&quot;заключение&quot; id=&quot;заключение&quot;&gt;Заключение&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

В заключении можно сказать, что подавляющее большинство программ могут быть одинаково эффективно написаны с использованием любой парадигмы (+/- килобайт, +/- мегагерц). А задачи, требующие конкретного подхода (будь то RTOS, или прерывания, или автомат состояний, или чистый ассемблер), появляются намного реже. Ниша, где RTOS может оказаться эффективней остальных парадигм -  сложные программы с большим количеством параллельных процессов, состояний и режимов работы. Но это не означает, что такие программы могут быть написаны только с использованием RTOS. Так же, как не означает и того, что все остальные программы должны быть написаны без нее.
&lt;/p&gt;

&lt;p&gt;
Зачастую проблема не в критериях выбора инструмента (или парадигмы программирования), а в умении конкретного программиста построить эффективную абстрактную модель и реализовать ее с помощью предпочитаемого им инструмента.
&lt;/p&gt;

&lt;p&gt;
&lt;br/&gt;
 
&lt;br/&gt;
 
&lt;br/&gt;
 
Виктор Тимофеев, февраль, 2011&lt;br/&gt;
 
&lt;a href=&quot;mailto:&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot; class=&quot;mail JSnocheck&quot; title=&quot;&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot;&gt;osa@pic24.ru&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Заключение&quot; [20190-] --&gt;</description>
    </item>
    <item rdf:about="http://www.pic24.ru/doku.php/osa/articles/rtos_usage?rev=1291812251">
        <dc:format>text/html</dc:format>
        <dc:date>2010-12-08T15:44:11+03:00</dc:date>
        <title>Программирование микроконтроллеров с использованием ОСРВ OSA</title>
        <link>http://www.pic24.ru/doku.php/osa/articles/rtos_usage?rev=1291812251</link>
        <description>


&lt;h1&gt;&lt;a name=&quot;программирование_микроконтроллеров_с_использованием_осрв_osa&quot; id=&quot;программирование_микроконтроллеров_с_использованием_осрв_osa&quot;&gt;Программирование микроконтроллеров с использованием ОСРВ OSA&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;

Виктор Тимофеев, &lt;a href=&quot;mailto:&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot; class=&quot;mail JSnocheck&quot; title=&quot;&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot;&gt;osa@pic24.ru&lt;/a&gt; (февраль, 2009)
&lt;/p&gt;

&lt;p&gt;
(ОСРВ - операционная система реального времени) 
&lt;/p&gt;

&lt;p&gt;
Два слова о том, что побудило написать эту статью. Хоть мне и не часто приходят письма с вопросами по ОСРВ OSA (отписываются не более 10 человек за месяц), тем не менее, этого достаточно, чтобы набрать статистику по часто совершаемым ошибкам. Я уже несколько раз отвечал на одни и те же вопросы и давал одни и те же советы, поэтому я решил все это дело обобщить в одной статье и выложить на всеобщее обозрение. Полагаю, что  статья окажется полезной не только тех, кто только начинают писать программы с использованием ОСРВ, но и для тех, кто уже имеют некоторый опыт. 
&lt;/p&gt;

&lt;p&gt;
Статья разделена на несколько частей: 
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#зачем_контроллеру_ось&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Зачем контроллеру &amp;quot;ось&amp;quot;?&lt;/a&gt;&lt;/span&gt; - здесь приводятся предпосылки и аргументы в пользу использования ОСРВ в некоторых проектах;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#общие_рекомендации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Общие рекомендации&lt;/a&gt;&lt;/span&gt; - некоторые рекомендации по использованию ОСРВ;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#faq_по_осрв_osa&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;FAQ по ОСРВ OSA&lt;/a&gt;&lt;/span&gt; - самые частые вопросы от программистов;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Советы по оптимизации&lt;/a&gt;&lt;/span&gt; - некоторые приемы и рекомендации по конфигурированию OSA для поднятия производительности системы;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#заключение&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Заключение&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Статья посвящена использованию ОСРВ в малоресурсных контроллерах. Однако, большинство рекомендаций (которые, конечно же, не бесспорны) могут быть приняты на вооружение применительно к остальным контроллерам.
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Программирование микроконтроллеров с использованием ОСРВ OSA&quot; [1-2779] --&gt;
&lt;h2&gt;&lt;a name=&quot;зачем_контроллеру_ось&quot; id=&quot;зачем_контроллеру_ось&quot;&gt;Зачем контроллеру &amp;quot;ось&amp;quot;?&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Зачем контроллеру ось?&quot; [2780-2840] --&gt;
&lt;h3&gt;&lt;a name=&quot;что_хорошего_дает_использование_осрв&quot; id=&quot;что_хорошего_дает_использование_осрв&quot;&gt;Что хорошего дает использование ОСРВ&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Так зачем же нужна операционная система в контроллерах? Я бы назвал несколько причин. 
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Во-первых, ОСРВ предоставляет программисту готовый планировщик&lt;/strong&gt; - некую подпрограмму, следящую за состоянием выполняемых задач, обеспечивающую поиск наиболее важной на данный момент и передачу ей управления. Поскольку часто на контроллер возлагается выполнение сразу нескольких задач (например, чтение клавиатуры, вывод данных на дисплей и пр.), то программист, создавая очередную программу, так или иначе вынужден каждый раз с нуля писать некий свой планировщик (в простейшем случае - это суперлуп, где все задачи-подпрограммы вызываются по очереди в бесконечном цикле). При этом в большинстве случаев не учитывается важность (приоритет) подпрограмм, вызываемых таким планировщиком, и подпрограмма, требующая больше внимания к себе со стороны планировщика, будет, тем не менее, получать его поровну со всеми остальными. Так вот, ОСРВ обеспечивает программиста уже готовым планировщиком, способным не только самостоятельно запускать все подпрограммы, но еще и определять, нужно ли какую-то конкретную подпрограмму запускать в данный момент, или нет, а также при обнаружении нескольких готовых к выполнению подпрограмм выбирать из них наиболее важную и передавать управление именно ей. 
&lt;em&gt;(Примечание: в ОСРВ такие подпрограммы называются задачами.)&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Во-вторых, ОСРВ обеспечивает параллельное выполнение всех задач&lt;/strong&gt;. В программе, написанной без применения ОСРВ, функция не может прерваться так, чтобы при повторном ее вызове продолжить свое выполнение с того места, где она была прервана. Разумеется, можно прибегнуть ко всякого рода хитростям, вроде таблицы переходов в начале функции или явного сохранения адреса возврата перед выходом, но зачем все это делать вручную, если ОСРВ уже имеет такую возможность? Кроме того, прибегая к таким сложным и нестандартным приемам программирования, как сохранение адреса, можно напортачить и налепить серьезных ошибок, незаметных на первый взгляд. Да и таблицы переходов в начале функции требуют постоянного &amp;quot;ухода&amp;quot; за ними: появляется новая точка выхода из функции - нужно добавить еще один переход в таблицу; убирается точка выхода - нужно убрать переход. Итак, ОСРВ предоставляет нам инструмент, позволяющий покинуть функцию-задачу в любом месте и оставляющий нам уверенность в том, что при следующем входе в задачу мы продолжим ее выполнение с того же места, откуда мы ее покинули.
&lt;/p&gt;

&lt;p&gt;
&lt;em&gt;(Примечание: здесь речь, конечно, идет о псевдопараллельности, т.е. когда все задачи выполянются по очереди, быстро сменяя друг друга; порядок очереди определяется приоритетами и готовностью задач к выполнению.)&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;В-третьих, ОСРВ берет на себя отсчет временных задержек&lt;/strong&gt;. Часто в программе нужно выдержать паузу (например при записи в EEPROM мы ждем 5-10 мс). Выполняя задержку явно, т.е., зацикливая программу до тех пор, пока какой-нибудь счетчик не достигнет нужного значения, мы блокируем работу всей программы на время выполнения этой задержки. Не страшно, если нам нужно подождать 10 мс после записи одного байта в EEPROM. А если нам нужно записать 100 байт? Придется &amp;quot;вешать&amp;quot; программу на секунду? ОСРВ же дает возможность организовать программу так, чтобы во время выполнения задержки одной задачей могли выполняться остальные задачи. Т.е. отсутствует время пустого простоя системы. Кроме того, часто в программах требуется ожидать некоего события, но так, чтобы была возможность прервать ожидание, если событие не происходит в течение какого-то времени. Простейший пример - набор номера в GSM-модеме и ожидание ответа. Мы не можем ждать вечно, поэтому дополняем программу неким механизмом, который по истечении заданного времени прервет ожидание. Для этого мы заводим отдельный таймер (тактируемый, например, в прерывании) и следим за его значением. В этом есть некоторые неудобства. Во-первых, на время ожидания  все остальные задачи блокированы; во-вторых, нужна отдельная глобальная переменная нужной размерности, имеющая осмысленное имя (например, ModemAnswerTimer); в-третьих, нужно не забыть все такие переменные инкрементировать в обработчике прерываний; есть еще в-четвертых и в-пятых, но это уже мелочи. ОСРВ же может обеспечить ожидание события с выходом по таймауту так, что оно будет лишено перечисленных недостатков, особенно - главного из них: система не будет простаивать впустую во время ожидания.
&lt;/p&gt;

&lt;p&gt;
&lt;p&gt;&lt;div class=&quot;noteclassic&quot;&gt;&lt;strong&gt;(Комментарий Alex B.)&lt;/strong&gt; Тут дело даже не в какой-то абстрактной блокировке остальных задач. Очень часто 5 мс это действительно незаметный отрезок. До тех пор, пока во вводных основной проблемой не станет энергопотребление. Несмотря на то, что устройство может быть не автономным, уменьшение интегрального потребления есть серьезный плюс к ТТХ вашего девайса.
&lt;/div&gt;&lt;/p&gt;
&lt;/p&gt;

&lt;p&gt;

&lt;strong&gt;В-четвертых&lt;/strong&gt;. В большинстве случаев подпрограммы, отвечающие за разные функциональные узлы, должны обмениваться между собой информацией. Например, подпрограмма, производящая опрос клавиатуры, должна сообщать остальным подпрограммам, какая кнопка была нажата/отпущена; а подпрограмма, выводящая информацию на экран, должна эту информацию откуда-то получать. Т.е. программа должна иметь какой-то механизм обмена информацией между внутренними функциями. Тут есть несколько вариантов: через аргументы функций, через глобальные переменные, наконец, через сервисы операционной системы (семафоры, сообщения, флаги). А как быть, когда одна задача должна получать данные от нескольких задач сразу, или когда какая-то задача отправляет данные быстрее, чем задача-получатель успевает их обрабатывать? Вот тут на помощь приходит очень &lt;strong&gt;полезный механизм ОСРВ - очереди сообщений&lt;/strong&gt;. Они позволяют задаче-отправителю отправлять следующее сообщение (какую-то полезную информацию), если предыдущее еще не успело обработаться задачей-получателем. Все это, конечно же, можно реализовать и без ОСРВ, но зачем выполнять уже выполненную работу, к тому же уже проверенную и отлаженную.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;В-пятых, использование ОСРВ улучшает читабельность и наглядность программного кода&lt;/strong&gt;. То, что реализуется без ОСРВ в 10-15 строках кода, с ОСРВ может быть записано одной строкой. Здесь я не буду приводить конкретных примеров, пока поверьте мне на слово, а когда дойдет дело до написания программы, сами убедитесь в том, что это так.
&lt;/p&gt;

&lt;p&gt;
Наконец, &lt;strong&gt;в-шестых&lt;/strong&gt;, последний аргумент в пользу применения ОСРВ - &lt;strong&gt;удобство написания программы, когда над ней трудятся несколько человек&lt;/strong&gt;. Применительно к PIC-контроллерам, конечно, это редкость, но тем не менее - это большой плюс.
&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Что хорошего дает использование ОСРВ&quot; [2841-14640] --&gt;
&lt;h3&gt;&lt;a name=&quot;что_мы_теряем_используя_осрв&quot; id=&quot;что_мы_теряем_используя_осрв&quot;&gt;Что мы теряем, используя ОСРВ&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

За все выше перечисленные полезности придется платить. 
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Платим мы в первую очередь памятью контроллера&lt;/strong&gt;. Сама ОСРВ требует под себя некоторые ресурсы, пусть небольшие, но все-таки. Чем больше параллельных задач, тем больше RAM потребуется для хранения их текущих состояний, адресов возврата и внутренних таймеров. Кроме того, система оперирует еще и своими внутренними переменными, такими как указатель на текущую задачу, лучший приоритет и пр. Ну, естественно, сами системные сервисы и планировщик требуют под себя программную память.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Во вторую очередь мы платим системным временем&lt;/strong&gt;. Поиск самой важной их готовых к выполнению задач не происходит мгновенно. На это требуется время. Чем больше задач, тем дольше будет выполняться поиск. Это следует учитывать при проектировании программы.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;В третью очередь, как ни печально, мы платим непереносимостью программного кода&lt;/strong&gt;. Т.е. переносимость программы с одной платформы на другую возможна только при том условии, что другая платформа так же поддерживается выбранной ОСРВ. Здесь конкретно для OSA выбор пока ограничен только PIC-контроллерами серий 10, 12, 14, 16, 18, 24, 30 и 33. Расширение планируется, но не сегодня и не завтра. Эта задача на перспективу. (&lt;em&gt;&lt;strong&gt;Примечание&lt;/strong&gt;: с версии 100311 OSA поддерживает 8-битные контроллеры AVR фирмы Atmel&lt;/em&gt;)
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Наконец, опасениями, что &amp;quot;код разработан не мной, а каким-то дядей&amp;quot;&lt;/strong&gt;, и кто знает, что у этого дяди было на уме. Я сам работал с чужими библиотеками и помню, что когда натыкался на какую-то ошибку, всегда в первую очередь грешил на библиотеки. Некоторые идут дальше: не в библиотеке - так в компиляторе, не в компиляторе - так в программаторе, не в программаторе - так в контроллере! И вот уже строчат письмо на support.microchip.com: &amp;quot;Я лажу нашел! В ваших контроллерах RA4 неправильно работает!&amp;quot; Однако, 99% всех ошибок - на совести программиста. Тем не менее, не исключены и ошибки библиотек, компиляторов, программаторов, наконец, самих контроллеров (не случайно ведь появляются документы ERRATA). И ОСРВ здесь - не исключение. Те, кто отписываются мне по OSA, бывает, обнаруживают неправильное поведение системы, помогают отлавливать баги. И чем больше народа отписывается, тем более безглючной становится &amp;quot;ось&amp;quot;. После отлова очередного бага (а они все хитрее и все трудноуловимее) всегда кажется, что вот теперь-то багов точно нет. Тем не менее, нет-нет, да обнаружится еще один (еще более хитрый). Поэтому вряд ли я когда-нибудь скажу: &amp;quot;Все, пользуйтесь. Багов нет!&amp;quot; (Да и ни один производитель ПО такого не скажет). Поэтому сидеть и ждать, когда система станет абсолютно безглючной, - это так никогда и не приступить к работе с ней. А попробовать стоит хотя бы из любопытства. Если и решите: &amp;quot;нет, это не для меня&amp;quot;, - то хотя бы, я уверен в этом, почерпнете для себя что-то новое.

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Что мы теряем, используя ОСРВ&quot; [14641-19666] --&gt;
&lt;h3&gt;&lt;a name=&quot;можно_ли_обойтись_без_осрв&quot; id=&quot;можно_ли_обойтись_без_осрв&quot;&gt;Можно ли обойтись без ОСРВ?&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Ответ однозначный: да, можно. При проектировании программы программист должен предварительно взвесить все выше перечисленные аргументы, свои аргументы, свои предпочтения, элементную базу и пр. и принять решение, что ему будет быстрее/удобнее/дешевле. 
&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Можно ли обойтись без ОСРВ?&quot; [19667-20197] --&gt;
&lt;h3&gt;&lt;a name=&quot;резюме&quot; id=&quot;резюме&quot;&gt;Резюме&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

&lt;p&gt;&lt;div class=&quot;noteimportant&quot;&gt;
ОСРВ содержит набор функций и инструментов, которые при написании программы без ОСРВ волей-неволей приходится каждый раз создавать вручную (планировщик, таймеры, сервисы обмена информацией). А создавать - это не только писать программы и подпрограммы, но еще и отлаживать их. ОСРВ же имеет весь этот набор уже отлаженный и готовый к применению. Тем самым программисту предоставляется возможность сконцентрироваться на решении конкретных проблемно-ориентированных задач, не отвлекаясь на системные задачи, которые берет на себя ОСРВ. Однако, необходимо помнить и о том, что сама операционная система требует под свои нужды ресурсы, и если в контроллере, на котором предполагается делать проект, их мало, то стоит предварительно хорошенько взвесить, потянет ли он эту программу, усиленную с ОСРВ.

&lt;/div&gt;&lt;/p&gt;
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Резюме&quot; [20198-21712] --&gt;
&lt;h2&gt;&lt;a name=&quot;общие_рекомендации&quot; id=&quot;общие_рекомендации&quot;&gt;Общие рекомендации&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Некоторые присылают мне свои проекты, написанные с использованием OSA, с просьбой помочь разобраться, почему программа виснет или ведет себя не так, как хотелось бы, и пр. При разборе этих проектов я заметил, что многие при написании программы совершают две основные ошибки:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; пытаются использовать ОСРВ там, где это не нужно;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; любой фрагмент программы, который возможно написать с использованием сервисов ОСРВ, пишут, используя сервисы ОСРВ.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Эти два момента будут рассмотрены в первую очередь, а далее будут даны еще несколько рекомендаций по использованию ОСРВ.
&lt;/p&gt;

&lt;p&gt;

&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#не_используйте_осрв_там_где_это_не_нужно&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Не используйте ОСРВ там, где это не нужно&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
 
&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#не_злоупотребляйте_использованием_сервисов_осрв&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Не злоупотребляйте использованием сервисов ОСРВ&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
 
&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#не_забывайте_о_прерываниях&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Не забывайте о прерываниях&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
 
&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#о_глобальных_переменных&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;О глобальных переменных&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
 
&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#как_разбить_программу_на_задачи&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Как разбить программу на задачи&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
 
&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#как_расставлять_приоритеты&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Как расставлять приоритеты&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
 

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Общие рекомендации&quot; [21713-23729] --&gt;
&lt;h3&gt;&lt;a name=&quot;не_используйте_осрв_там_где_это_не_нужно&quot; id=&quot;не_используйте_осрв_там_где_это_не_нужно&quot;&gt;Не используйте ОСРВ там, где это не нужно&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Далеко не все задачи удобно решать с помощью ОСРВ (а в некоторых случаях ОСРВ даже будет мешать). Кроме того, следует помнить, что микроконтроллеры (в особенности те, на которые в первую очередь была ориентирована OSA, т.е. PIC10, PIC12, PIC16) имеют дефицит ресурсов. Некоторые программисты после написания одной-двух программ с использованием ОСРВ так привязываются к ней, что, формулируя для себя очередную задачу еще в проблемно-ориентированных терминах, уже сразу пытаются подогнать эту формулировку под принципы ОСРВ. Т.е., фактически, при проектировании программы пропускается важный этап - выбор инструмента. Это не совсем правильно; вернее - совсем неправильно.
&lt;/p&gt;

&lt;p&gt;
Например, простая задача для PIC10: &amp;quot;пока на входе контроллера есть меандр 1 КГц, горит зеленый светодиод; если частота меандра выходит за пределы допуска +/- 10% - гасим зеленый светодиод и зажигаем красный&amp;quot;. Здесь использование ОСРВ совсем не оправдано, более того, просто вредно, поскольку работа планировщика отнимет скоростные ресурсы контроллера и не позволит обеспечить требуемую точность. Тем не менее, некоторые (не буду говорить, кто) создадут 3 независимых задачи: одна следит за меандром, вторая обрабатывает светодиоды, третья следит за первыми двумя  - и будут усердно пытаться всю остальную программу оформить и утрамбовать так, чтобы эти три задачи справлялись со своими функциями, причем - в ущерб наглядности текста, прозрачности алгоритма и, наконец, надежности самой программы.
&lt;/p&gt;

&lt;p&gt;
Другой пример - какую-нибудь простую задачу затолкают в малоресурсный контроллер вместе с ОСРВ, на этапе компиляции обнаружат, что памяти (или ROM, или RAM, или и той и другой) не хватает, и начинают вытворять какие-то немыслимые фокусы по оптимизации вплоть до использования машинных кодов в inline-ассемблере (есть такая возможность в HT-PICC), чтобы программу вообще хоть как-то запустить (Михаил, без обид :) ), при этом даже не рассмотрев вариант написания программы без использования ОСРВ.
&lt;/p&gt;

&lt;p&gt;
В ответ на мои рассуждения о ОСРВ и малоресурсных контроллерах мне приводят в пример мою же программу led3, мол, она-то с использованием ОСРВ написана; зачем тогда такой пример, если Вы сами не рекомендуете так делать? Здесь ответ простой. Представьте, что Вы пришли на базар, чтобы купить топор для колки дров. Продавец, демонстрируя свои топоры, одним из их разрубает стальной трос. На топоре при этом - ни зазубрины. Это не значит, что все стальные тросы нужно непременно рубить этими топорами, забыв при этом о специальных для таких целей инструментах, но это кое-что говорит о возможностях этих топоров в работе по дереву. Так же и с этим примером для PIC10. Он приведен не для того, чтобы показать, что любая программа для любого контроллера может и должна быть написана под операционной системой; он приведен для того, чтобы показать, насколько конкретная ОСРВ нетребовательна к ресурсам (в частности, ни jacOS ни Salvo с такой задачей не справятся). Другими словами, этот пример показывает, что само ядро операционной системы будет почти незаметно для программы при использовании контроллера помощнее, например, PIC16F628 (не говоря уже о PIC18 и выше).
&lt;/p&gt;

&lt;p&gt;
Резюме такое: разумно выбирайте инструмент для решения задачи. 
Программу, которая может получиться, следует оценивать не только по основным параметрам: 
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &amp;quot;выполняет возложенные на нее функции&amp;quot;;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &amp;quot;лишь бы влезла в выбранный контроллер&amp;quot;;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &amp;quot;использованы продвинутые решения (ОСРВ)&amp;quot;. &lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Важно учитывать также второстепенные: 
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; наглядность (читабельность) исходников;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; время написания программы;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; удобство (и написания, и отладки);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; прозрачность алгоритма;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; возможность расширения программы (добавления каких-то новых функций);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; отсутствие каких-либо условностей (типа, у меня таймер никогда не будет успевать переполняться, потому что тра-та-та и бла-бла-бла)&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;p&gt;&lt;div class=&quot;noteimportant&quot;&gt;
Поэтому, если на этапе проектирования есть сомнения в том, что использование ОСРВ не совсем подходит для решения данной задачи, - лучше работать без нее. Сэкономите и время и нервы.

&lt;/div&gt;&lt;/p&gt;
&lt;/p&gt;

&lt;p&gt;
Есть религиозные войны &amp;quot;Си &amp;amp; асм&amp;quot;. Изучение перепалок на эту тему позволяет сделать вывод, что самые ярые &amp;quot;вояки&amp;quot; - это те, которые с противоположной стороной вопроса не очень знакомы. Возможно, на подходе религиозные войны &amp;quot;RTOS &amp;amp; !RTOS&amp;quot;. Я предлагаю не участвовать на передовой, ударяя себя пяткой в грудь и крича, что RTOS - сила, а осваивать и ОСРВ, и суперлуп, и прерывания (есть и такой вариант), и выбирать оптимальный для решения конкретной задачи.
&lt;/p&gt;

&lt;p&gt;
(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#общие_рекомендации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку общих рекомендаций&lt;/a&gt;&lt;/span&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Не используйте ОСРВ там, где это не нужно&quot; [23730-31913] --&gt;
&lt;h3&gt;&lt;a name=&quot;не_злоупотребляйте_использованием_сервисов_осрв&quot; id=&quot;не_злоупотребляйте_использованием_сервисов_осрв&quot;&gt;Не злоупотребляйте использованием сервисов ОСРВ&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

В присланных мне проектах я также замечал маниакальное использование сервисов ОСРВ везде: где нужно и где не нужно. Складывается такое ощущение, что некоторые программисты рассматривают ОСРВ как язык программирования, просто еще более высокого уровня, чем Си. И как при программировании на Си избегают использования встроенного ассемблера, так и при программировании под ОСРВ избегают использования самого Си. В результате такого подхода исходный текст программы сильно пестрит изобилием сервисных вызовов, которое просто затрудняет чтение программы. Применительно к OSA, возможно, есть и моя вина в том, что некоторые так пишут программы. OSA предоставляет изобилие сервисов для, в общем-то, простых операций, не требующих каких-то сложных пассажей с передачей управления ядру (достаточно посмотреть на сервисы установки/сброса флагов). По существу же, большое количество сервисов - это просто формализация простых операций в терминах конкретной ОС. Применительно к флагам эта формализация звучит так: есть сервисы ожидания флагов, значит должны быть и сервисы установки/сброса флагов (хотя по сути - это простые побитовые операции and и or) и их проверки ( == и != ). Т.е. есть некий объект ОС (флаг), следовательно, ОС должна обеспечить программиста сервисами по всевозможным операциям с объектом. На деле же получается довольно трудночитаемый текст.
&lt;/p&gt;

&lt;p&gt;
Однако, это не единственная причина такого яростного пичкания программы системными сервисами. Тут какой-никакой здравый смысл присутствует. Это вроде как стандартизация, все-таки флаг - объект ОС, и неизвестно, как там нутро этой ОС устроено и как оно его обрабатывает (не все же программисты заглядывают в исходники ОС, даже если они открыты). Есть еще одна причина: мышление в контексте ОСРВ заставляет людей местами необдуманно использовать сервисы ОС там, где это совсем не к месту. 
&lt;/p&gt;

&lt;p&gt;
В качестве примера можно привести использование счетного семафора в качестве счетчика цикла. Он, конечно, может применяться в этих целях, принципами ОСРВ это не возбраняется, но здравый смысл должен говорить за неэффективность такого подхода. Сравните два приведенных ниже фрагмента кода:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    OS_Csem_SetValue&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;csem, &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        OS_Csem_Accept&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;csem&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;OS_Csem_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;scem&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;--&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Здесь очевидно злоупотребление сервисами ОС. Возможно, этот пример кажется утрированным, тем не менее, это фрагмент реальной программы.
&lt;/p&gt;

&lt;p&gt;
Еще могу привести такой пример. В одной из присланных мне программ была подпрограмма приема данных по последовательному интерфейсу (не аппаратному). Там была заведена переменная mask, которая имела только один установленный бит и циклически сдвигалась после приема каждого импульса. А принимаемый байт записывался в системную переменную типа OST_FLAG. В результате код имел несколько комичный вид:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;co1&quot;&gt;// CLC - ножка CLOCK&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;// DIO - ножка DATA&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;// (прим. - В.Т.)&lt;/span&gt;
&amp;nbsp;
    OS_Cond_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;CLC&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DIO&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;  OS_Flag_Set_1&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;data, mask&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;      OS_Flag_Set_0&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;data, mask&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    mask &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;mask&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; ...&lt;/pre&gt;
&lt;p&gt;
Налицо совершенно бессмысленное использование сервиса &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Flag_Set_x, которое в данном случае не только не наглядно, но еще и сбивает с толку. Куда нагляднее выглядел бы код:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    OS_Cond_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;CLC&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DIO&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;  data |=  mask;
    &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;      data &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; ~mask;
&amp;nbsp;
    mask &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;mask&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; ...&lt;/pre&gt;
&lt;p&gt;
Еще в одной присланной мне программе человек так увлекся бинарными семафорами, что на них построил всю логику работы программы. У него было под 40 этих семафоров, и в результате он в них запутался, т.к. его программа выглядела уже не как последовательность действий, а как какая-то МДНФ на ПЛМ, которая хоть и выполняет свою функцию, но без пристальнейшего вглядывания имеет совсем непонятную логику работы. И модификация такой программы становится весьма затруднительной (Семен, это я о тебе :) ).
&lt;/p&gt;

&lt;p&gt;
Я могу привести еще несколько подобных примеров, но полагаю, что читающий и так понял, к чему я призываю. Самыми полезными сервисами ОСРВ являются сервисы ожидания и переключения контекста. Все остальное - формализованное дополнение.
&lt;/p&gt;

&lt;p&gt;
Поэтому я подведу итог: 
&lt;p&gt;&lt;div class=&quot;noteimportant&quot;&gt;
Сервисы ОСРВ должны применяться по принципу &amp;quot;если без них никак&amp;quot;, а не по принципу &amp;quot;если можно как-то извернуться и сделать это на сервисах ОС, то надо сделать именно так&amp;quot;. Программа не должна выглядеть как номенклатурный список складского учета, где все товары имеют идентификационный код, начинающийся с &amp;quot;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_&amp;quot;, она должна быть наглядной и понятной.

&lt;/div&gt;&lt;/p&gt;
&lt;/p&gt;

&lt;p&gt;
(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#общие_рекомендации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку общих рекомендаций&lt;/a&gt;&lt;/span&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Не злоупотребляйте использованием сервисов ОСРВ&quot; [31914-40001] --&gt;
&lt;h3&gt;&lt;a name=&quot;не_забывайте_о_прерываниях&quot; id=&quot;не_забывайте_о_прерываниях&quot;&gt;Не забывайте о прерываниях&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
Также распространенной ошибкой при проектировании является отказ от использования кода в прерываниях. Многие программисты пытаются всю программу представить непременно в виде задач ОС, полагая при этом, что прерывание не оформить как отдельную задачу, и, следовательно, внутри него ничего делать нельзя. В большинстве программ, присланных мне, код прерыввания выглядел примерно одинаково:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; interrupt isr &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    OS_EnterInt&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;T0IF&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
       T0IF &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
       OS_Timer&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
    OS_LeaveInt&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Разница была только в используемом таймере. Все же остальные обработчики прерываний были вынесены в отдельные задачи и выглядели примерно так:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task_Iinterrupt_INTF &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        OS_Cond_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;INTF&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        INTF &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
        &lt;span class=&quot;coMULTI&quot;&gt;/* Далее следует ко добработки прерывания */&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Неэффективность такого подхода очевидна:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; во-первых, теряются преимущества использования системы прерываний, а именно - скорость реакции на событие, вывод контроллера из sleep-режима, маскирование; &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; во-вторых, появляются несколько лишних задач, каждая из которых требует ресурсы: память и время (об этом будет сказано чуть ниже в разделе &amp;quot;&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#как_разбить_программу_на_задачи&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Как разбить программу на задачи&lt;/a&gt;&lt;/span&gt;&amp;quot;)&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;p&gt;&lt;div class=&quot;noteimportant&quot;&gt;
ОСРВ призвана упростить процесс проектирования и написания программы, а не заменить какие-то аппаратные модули микроконтроллера (в частности - контроллер прерваний). Поэтому не нужно стесняться использовать систему прерываний, укоряя себя за то, что &amp;quot;это же не по правилам РТОС!&amp;quot;. Все по правилам.  

&lt;/div&gt;&lt;/p&gt;
&lt;/p&gt;

&lt;p&gt;
В дополнение напомню, что ОСРВ OSA имеет целый набор сервисов для работы внутри прерываний, позволяя вести обмен информацией с остальной программой в терминах ОСРВ.
&lt;/p&gt;

&lt;p&gt;
(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#общие_рекомендации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку общих рекомендаций&lt;/a&gt;&lt;/span&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Не забывайте о прерываниях&quot; [40002-43180] --&gt;
&lt;h3&gt;&lt;a name=&quot;о_глобальных_переменных&quot; id=&quot;о_глобальных_переменных&quot;&gt;О глобальных переменных&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Вопрос использования глобальных переменных довольно спорный. Одни утверждают, что глобальные переменные - это зло, т.к. их наличие усложняет переносимость отдельных функций, а также не исключает возможные коллизии из-за модификаций глобальной переменной в разных модулях и т.д. Другие говорят, что использование глобальных переменных сокращает ресурсозатраты на обмен информацией. Точно сказать, кто прав в этом вопросе, я думаю, нельзя. И те и другие по-своему правы. Нужно рассматривать каждый конкретный случай в отдельности, при этом следует принимать во внимание не только задачу, под которую пишется программа, но еще и специфику используемого контроллера, и особенности используемого компилятора, и сколько программистов трудятся над задачей и т.д. и т.п. 
&lt;/p&gt;

&lt;p&gt;
В идеале программа не должна содержать глобальных переменных. В реальности же нам приходится искать компромисс между структурностью программы и наличием свободных ресурсов (памяти и скорости). Создавая программу с помощью ОСРВ, можно, конечно, все обмены данными между задачами делать с помощью объектов ОС: семафоров, сообщений, очередей и пр. (все эти объекты, вообще-то, сами по себе являются глобальными переменными, но они имеют четкую формализацию в терминах используемой ОС, а потому сильно упрощают перенос функций в другие программы и практически исключают коллизии с модификацией переменных разными модулями). Тем не менее, такой подход не всегда оправдан по нескольким причинам:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; одна переменная может использоваться сразу несколькими задачами (например, переменная, показывающая текущий режим работы устройства); при каждом изменении такой переменной пришлось бы сообщать об этом сразу нескольким задачам, что накладно по ресурсам;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; программа окажется сильно перегруженной системными вызовами, что затруднит ее читабельность;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; усложняется логика самой программы;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; наконец, сами сервисы требуют под себя какие-то ресурсы, и с увеличением сервисных вызовов у нас уменьшается память под саму программу.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Поэтому в большинстве случаев, особенно, когда дело касается малоресурсных контроллеров, имеет смысл некоторые переменные делать глобальными. Тут еще раз напомню, что сервисы ОС лучше использовать только там, где без них никак, а не там, где их можно втиснуть. Хоть это утверждение и спорно в общем случае, но применительно к контроллерам я считаю такой подход правильным.
&lt;/p&gt;

&lt;p&gt;
(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#общие_рекомендации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку общих рекомендаций&lt;/a&gt;&lt;/span&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;О глобальных переменных&quot; [43181-47707] --&gt;
&lt;h3&gt;&lt;a name=&quot;как_разбить_программу_на_задачи&quot; id=&quot;как_разбить_программу_на_задачи&quot;&gt;Как разбить программу на задачи&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
Эта задача не совсем тривиальна. Даже в каждом конкретном случае может быть несколько решений. В присланных мне проектах четко прослеживались две основные ошибки, совершаемые программистами при разбиении программы на задачи. Вернее сказать, не ошибки, а, как бы выразиться, две крайности, в которые бросаются программисты.
&lt;/p&gt;

&lt;p&gt;
Первая - программист пытается разбить программу на задачи так, чтобы каждая задача выполняла одно свое действие и не мешала работать другим. Хоть в теории такое решение и выглядит красиво, но на практике получается совсем не так. Иногда дело доходит до того, что на каждое реле, управляющее каким-то силовым выходом, заводится отдельная задача. Выглядит она довольно красиво:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task_Relay1 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        OS_Bsem_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;BS_RELAY1&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        RELAY1 &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
        OS_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        RELAY1 &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;

Но что у нас получается на практике? Если программа управляет десятком таких силовых выходов, то у нас заводится десять таких задач. С виду - все красиво и аккуратно. Но не следует забывать, что каждая активная задача в ОСРВ требует несколько байт оперативной памяти, где хранится ее текущее состояние, адрес возврата, ее таймер (все эти параметры хранятся в так называемом дескрипторе задачи). Например, для PIC16 размер дескриптора задачи 5 байт (он может быть и меньше, но мы рассматриваем общий случай), и для десяти задач потребуется 50 байт ОЗУ, что для PIC16 - просто расточительство. Кроме того, еще одна проблема, возникающая при большом количестве задач, - увеличивается время работы планировщика, т.е. то время, за которое планировщик успеет перебрать все задачи, определить, какие из них готовы к выполнению, и из этих готовых найти самую важную (т.е. имеющую высший приоритет). 
&lt;/p&gt;

&lt;p&gt;
Так же к недостаткам такого подхода можно отнести и то, что усложняется управление этими задачами. Чем больше задач требуют каких-то данных для своей работы (в нашем примере это всего лишь бинарные семафоры, но ведь некоторые задачи могут требовать и сообщения, и очереди сообщений), тем остальным задачам сложнее будет справляться с управлением такими потоками данных. 
&lt;/p&gt;

&lt;p&gt;
Можно привести еще пример (тоже из реальной жизни): на экран нужно было выводить информацию разного характера. Для этого были созданы 4 задачи, каждая из которых выводила на экран свой параметр. Каждая задача ожидала от головной задачи свое сообщение. В результате, помимо того, что для 4-х задач функции вывода на экран являлись разделяемым ресурсом, так еще и &lt;em&gt;головная задача была озадачена головной болью&lt;/em&gt; по работе с четырьмя сообщениями. Кроме того, такой подход накладывает ограничение на содержание выводимых на экран данных, ибо для вывода 5-го параметра потребовалась бы 5-я задача (и 5-е сообщение), для 6-го - 6-я и т.д. 
&lt;/p&gt;

&lt;p&gt;
Вторая крайность - это когда программист пытается все, что не является обработкой кнопок, затолкать в одну задачу. Она занимается и приемом даных по USART, и записью данных в EEPROM, и … да чем только не занимается! Зато информацию о кнопках получает извне. Зато есть многозадачность! Бессмысленность такого подхода очевидна (тем не менее, он часто встречается): нет преимущества использования ОСРВ. В чем преимущество параллельности выполнения задач, если задача всего одна? В чем преимущество очередей сообщений, если задача сообщения отсылает сама себе? Какова будет реакция на событие, если задача занимается сразу всем? Про читабельность кода, написанного с таким подходом, я вообще молчу. Единственное, что полезного можно ухватить от ОСРВ в таком случае, - это удобство формирования задержек (&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay - и все!).
&lt;/p&gt;

&lt;p&gt;
Итак, как же разбивать программу на задачи? Как я уже писал выше, единой рекомендации по этому вопросу нет. Тут я могу дать только несколько советов, исходя из опыта и здравого смысла. 
&lt;/p&gt;

&lt;p&gt;
Для начала приведу очевидный пример: сначала программа была написана под конкретный LCD-индикатор. По прошествии какого-то времени после запуска ее в серию в продаже появился новый менее дорогой и более функциональный индикатор, и руководство фирмы приняло решение следующую версию устройства делать на нем. Очевидно, что если при написании программы все, относящееся к функциям вывода на экран, было вынесено в отдельный модуль (отдельную задачу), то и модификация программы будет минимальной. Т.е. меняем только функции работы с LCD и задачу вывода информации на экран (единственную задачу, которая работает с функциями LCD). Для остальной программы такая замена будет незаметной: она как отсылала свои сообщения в очередь, так и отсылает, а что там вытворяет с ними задача вывода на экран, - это уже их не касается.
&lt;/p&gt;

&lt;p&gt;

&lt;p&gt;&lt;div class=&quot;noteimportant&quot;&gt;
Другими словами: если какой-то внешний модуль допускает замену, то управление этим модулем нужно выносить в отдельную задачу. К таким модулям, помимо индикаторов, можно также отнести внешнюю память, протоколы обмена данными, клавиатуру, GPS- и GSM-модули и т.д.

&lt;/div&gt;&lt;/p&gt;
&lt;/p&gt;

&lt;p&gt;
Рассмотрим теперь такой пример. Допустим, программист работает в фирме, которая разрабатывает, скажем, системы доступа: домофоны, кодовые замки, пульты дистанционного управления и пр.
&lt;/p&gt;

&lt;p&gt;
Почти в каждом из разрабатываемых фирмой устройств есть кнопки. И чтобы не писать функции обработки кнопок для каждого такого устройства, есть смысл вынести обработку кнопок в отельную задачу, оформить ее в виде отдельного файла и использовать во всех проектах (изменяя только количество кнопок, полярность и пр.).
&lt;/p&gt;

&lt;p&gt;

&lt;p&gt;&lt;div class=&quot;noteimportant&quot;&gt;
Итак, если предполагается в будущем какой-то кусок текущей программы использовать еще где-то, то такой кусок было бы удобно оформить в виде отдельной задачи.

&lt;/div&gt;&lt;/p&gt;
&lt;/p&gt;

&lt;p&gt;
Не нужно объединять в одной задаче функционально различные модули. Например объединять вывод звука с чтением данных по UART. Пускай даже и удастся написать задачу так, что эти функции не будут мешать и задерживать друг друга, но концептуально это неправильно, т.к. программа теряет наглядность. Т.е. лучше делать задачу максимально наглядной.
&lt;/p&gt;

&lt;p&gt;
При вынесении каких-то функций в отдельную задачу нужно предварительно прикинуть, сможет ли задача при такой организации обеспечить реакцию на событие в течение определенного времени. Это время может быть различным в зависимости от события. Например, если пользователь нажимает на кнопку, то он хочет увидеть реакцию устройства сразу же, и задержка всего в секунду уже недопустима. Если событие - это превышение температуры в комнате на 2 градуса выше какой-то нормы, то тут допустима задержка реакции в 5 секунд или в минуту - разницы особой нет. Т.е. задача должна быть спроектирована таким образом, чтобы ее выполнение не мешало ей же реагировать на события. Например, у нас есть задача, управляющая несколькими силовыми приборами: свет, насосы, обогреватель. Получая сообщение о том, что нужно на 10 секунд включить насос, она в течение этих 10 секунд может оказаться не в состоянии обработать другие приходящие ей сообщения. Если логика работы такой задачей проста, то эту проблему можно обойти, добавив в программу несколько таймеров и булевых переменных. Однако же, если логика посложнее, то тогда, наверное, есть смысл разбить эту задачу на две или более.
&lt;/p&gt;

&lt;p&gt;
В одном из присланных мне проектов был реализован интересный подход, который тоже можно взять на вооружение. В программе было описано около 20 функций-задач. Но в один момент времени могли работать только 4. 3 из них работали постоянно, а 4-я в зависимости от текущего режима все время пересоздавалась. Преимущество такого подхода - скорость работы планировщика (чем меньше активных задач, тем планировщик OSA работает быстрее). Недостаток - нужно очень внимательно следить за создаваемыми/удаляемыми задачами, а так же за тем, чтобы при удалении они не забывали освобождать занимаемые ресурсы.
&lt;/p&gt;

&lt;p&gt;

Попробую подытожить рекомендации. Написанная программа должна обладать следующими свойствами:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; она должна правильно выполнять возложенные на нее функции. Это свойство к разбиению программы на задачи отношения, в общем-то, не имеет, но если программа не обладает этим свойством, то нет смысла рассматривать все остальные. Поэтому для полноты картины это свойство оставим на первом месте.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; она должна успевать обрабатывать события в заданный промежуток времени. Следовательно, при проектировании задач должно быть учтено время работы самой задачи.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; она должна быть дружественной программисту. Не только в том смысле, что программист при написании соблюдал все правила оформления исходных текстов (отступы, пробелы, пустые строки, выровненные комментарии и т.п.), но и в том, чтобы, глядя в программу, можно было бы прочитать алгоритм работы устройства.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; она должна быть модульной. Т.е., во-первых, иметь возможность быстрой замены одного модуля другим (как в приведенном выше примере про LCD-индикаторы), и во-вторых, оставлять возможность использовать уже написанные куски кода в других приложениях.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; она не должна содержать слишком много простых задач или слишком мало сложных.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Напомню, что все выше перечисленное носит исключительно рекомендательный характер. Возможно, кто-то руководствуется другими правилами. Тем не менее, часто приходится видеть программы людей, которые каким-либо правилам следуют условно (т.е. не следуют им совсем), поэтому я надеюсь, что эти рекомендации кому-то окажутся полезными.
&lt;/p&gt;

&lt;p&gt;
(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#общие_рекомендации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку общих рекомендаций&lt;/a&gt;&lt;/span&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Как разбить программу на задачи&quot; [47708-64375] --&gt;
&lt;h3&gt;&lt;a name=&quot;как_расставлять_приоритеты&quot; id=&quot;как_расставлять_приоритеты&quot;&gt;Как расставлять приоритеты&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Применительно к кооперативной ОСРВ, приоритет задачи - понятие весьма условное. Оно больше подходит к вытесняющим ОСРВ, где планировщик может приостановить задачу в любой момент, чтобы передать управление готовой более приоритетной задаче, что обеспечивает выполнение важного условия - детерминированного времени реакции на событие. В кооперативной ОСРВ вытеснение произойти не может, т.е. планировщик не может отнять у задачи управление, а только сама задача может решить, отдавать управление планировщику или нет. Поэтому приоритетность в кооперативных ОСРВ не совсем уместна. 
&lt;/p&gt;

&lt;p&gt;
Кроме того, включение механизма приоритетов несколько замедляет работу планировщика, поскольку, помимо перебора всех задач в поиске готовой, он будет должен еще заниматься сравниванием их приоритетов и поиском максимального. Поэтому, как это ни парадоксально, работая в приоритетном режиме, задача с высоким приоритетом может получить управление с большей задержкой, чем если бы приоритеты были отключены.
&lt;/p&gt;

&lt;p&gt;
Тем не менее, в программах под кооперативными ОСРВ бывают случаи, когда приоритетность выручает.
&lt;/p&gt;

&lt;p&gt;
Например, при сильной загрузке контроллера. Если контроллер сильно загружен какими-то вычислениями, или общением с внешними устройствами, чтением датчиков и т.д., то возникают ситуации, когда он не в состоянии, выполняя все задачи по очереди, обеспечить реакцию на некоторые события в приемлемое время. Включение приоритетного режима позволит быстрым высокоприоритетным задачам выполняться чаще остальных, позволяя сократить время реакции.
&lt;/p&gt;

&lt;p&gt;
Другой пример: при происшествии какого-то события нам нужно выполнить несколько задач в определенной последовательности. Все задачи находятся в ожидании установки какого-то бита (пусть это будет флаг прерывания INTF). Тогда расстановка приоритетов задачам позволит нам однозначно определить порядок выполнения задач при установке INTF. Последняя задача, отработавшая по этому событию (т.е. самая низкоприоритетная), этот флаг сбросит.
&lt;/p&gt;

&lt;p&gt;
В большинстве же случаев в программах под кооперативной ОСРВ приоритеты задачам не нужны. Однако, если все же решили использовать приоритетный режим, то как тогда правильно расставить приоритеты? 
&lt;/p&gt;

&lt;p&gt;
Для того, чтобы ответить на этот вопрос, нужно посмотреть на задачи не как программист, например, &amp;quot;хочу, чтобы опрос кнопок производился с интервалом 20 мс&amp;quot;, а как пользователь, т.е. &amp;quot;хочу, чтобы при нажатии на кнопку, сразу происходило действие&amp;quot;. При такой постановке вопроса становится ясно, что не столь важно опрашивать кнопку именно каждые 20 мс, сколь успеть ее обработать сразу при нажатии. При этом уже очевидно, что интервал может быть и 20 мс и 40 мс, т.е. не так критичен, а отсюда становится понятно, что приоритет этой задачи нет смысла делать высоким. 
&lt;/p&gt;

&lt;p&gt;
Высокоприоритетными задачами нужно делать те, которые действительно критичные ко времени реакции. Представьте себе устройство управления автоматическими воротами. Задачами этого устройство являются: прием по радиоканалу команд от ПДУ на открытие/закрытие, управление двигателями дверей, а также - аварийный реверс двигателей, если на пути створок дверей оказалось препятствие. Последняя задача является критичной ко времени. Например, оператор нажал копку на ПДУ - ворота открылись, машина поехала, а оператор нажал кнопку закрытия ворот раньше времени. В результате створки ворот упрутся в кузов автомобиля. Чем быстрее устройство сообразит, что надо дать дверям обратный ход, тем дешевле будут последствия.
&lt;/p&gt;

&lt;p&gt;
Не следует давать высокий приоритет задачам, которые долго выполняются. Например, в программе одновременно оказались готовыми к выполнению задача включения реле и задача преобразования Фурье. Очевидно, что вторая заберет ресурсы контроллера на долгое время, поэтому есть смысл пропустить вперед быструю задачу включения реле, после чего уже можно отдавать управление вычислителю.
&lt;/p&gt;

&lt;p&gt;
Ко всему сказанному можно добавить, что большинство ОСРВ (OSA - не исключение) позволяют менять приоритет задач в ходе выполнения программы, что может в некоторых случаях оказаться полезным. Например, нет смысла давать высокий приоритет задаче записи на flash-карту в фотоаппарате, если сам фотоаппарат находится в режиме просмотра снимков, и запись в данный момент ему нужна только для сохранения каких-то единичных параметров, вроде текущей яркости экрана, или номера последнего просмотренного снимка. И наоборот, задачу записи во flash-память лучше сделать самой приоритетной во время сохранения снимка, пожертвовав при этом скоростью вывода на экран, скоростью обработки кнопок и т.д.
&lt;/p&gt;

&lt;p&gt;
Вот, собственно, основные рекомендации по расстановке приоритетов. В целом эта задача так же не тривиальна, как и разбиение программы на задачи, и в каждом случае нужно решать ее отдельно. Но рекомендации, приведенные выше, помогут упростить этот процесс. 
&lt;/p&gt;

&lt;p&gt;
(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#общие_рекомендации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку общих рекомендаций&lt;/a&gt;&lt;/span&gt;)
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Как расставлять приоритеты&quot; [64376-73273] --&gt;
&lt;h2&gt;&lt;a name=&quot;faq_по_осрв_osa&quot; id=&quot;faq_по_осрв_osa&quot;&gt;FAQ по ОСРВ OSA&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Здесь я собрал ответы на самые распространенные вопросы по OSA.
&lt;/p&gt;

&lt;p&gt;
&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#можно_ли_вызывать_сервисы_ожидания_из_функций_вызываемых_задачами&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Можно ли вызывать сервисы ожидания из функций, вызываемых задачами?&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
 
&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#ожидание_события_в_цикле&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Ожидание события в цикле&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
 
&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#можно_ли_создать_задачу_по_указателю_на_функцию&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Можно ли создать задачу по указателю на функцию?&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
 
&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#что_будет_если_отсылатьпринимать_сообщения_из_неинициализированной_очереди&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Что будет, если отсылать/принимать сообщения из неинициализированной очереди?&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
 
&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#изменение_типов_сообщений&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Изменение типов сообщений&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
 
&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#если_две_задачи_ожидают_одного_и_того_же_события&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Если две задачи ожидают одного и того же события&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
 
&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#модификация_тела_сообщения_до_того_как_оно_будет_принято&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Модификация тела сообщения до того, как оно будет принято&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
 
&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#использование_таймеров_вне_задач&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Использование таймеров вне задач&lt;/a&gt;&lt;/span&gt;&lt;br/&gt;
 
&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#почему_виснет_os_delay&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Почему виснет OS_Delay?&lt;/a&gt;&lt;/span&gt;

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;FAQ по ОСРВ OSA&quot; [73274-75200] --&gt;
&lt;h3&gt;&lt;a name=&quot;можно_ли_вызывать_сервисы_ожидания_из_функций_вызываемых_задачами&quot; id=&quot;можно_ли_вызывать_сервисы_ожидания_из_функций_вызываемых_задачами&quot;&gt;Можно ли вызывать сервисы ожидания из функций, вызываемых задачами?&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

&lt;strong&gt;Ответ: нет.&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
Этот вопрос, пожалуй, задают чаще остальных. Такой подход, действительно, выглядит очень заманчивым, когда, например, в задачах часто вызывается сервис &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay  с одним и тем же параметром:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;co1&quot;&gt;// В этом примере просто напрашивается вынос OS_Delay(10) в отдельную функцию.&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;// Сам вызов сервиса занимает около 10 слов ROM, и его, конечно, хочется&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;// заменить одним вызовом.&lt;/span&gt;
    ...
    &lt;span class=&quot;me1&quot;&gt;OS_Delay&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    ...
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;...&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; OS_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    ...
    &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        ...
        &lt;span class=&quot;me1&quot;&gt;OS_Delay&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        ...
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;...&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    ...&lt;/pre&gt;
&lt;p&gt;
или когда есть несколько задач, ожидающих одно и то же событие:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    ...
    &lt;span class=&quot;co1&quot;&gt;// Ждем, когда освободится доступ в EEPROM&lt;/span&gt;
    OS_Bsem_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;BS_EEPROM_FREE&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    ...&lt;/pre&gt;
&lt;p&gt;
К сожалению в ОСРВ OSA такое недопустимо. Дело в том, что при таком подходе произойдет путаница с адресами возврата в стеке. Рассмотрим на примере:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co1&quot;&gt;//-----------------------------------------------&lt;/span&gt;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Delay10 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    OS_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//-----------------------------------------------&lt;/span&gt;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Bsem_Wait &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    OS_Bsem_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;BS_BINSEM&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//-----------------------------------------------&lt;/span&gt;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; TaskA &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        Delay10&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        Delay10&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        Delay10&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//-----------------------------------------------&lt;/span&gt;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; TaskB &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        Bsem_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        Bsem_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        Bsem_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//-----------------------------------------------&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Итак, у нас есть две задачи: TaskA и TaskB. Обе вызывают разные функции, в каждой из которых есть сервис, содержащий код возврата в планировщик (в перечне сервисов все такие сервисы в примечании отмечены буквой &amp;quot;T&amp;quot;). Для простоты рассмотрим работу этого примера на PIC16. &lt;em&gt;(Примечание: планировщик в этих контроллерах передает управление задачам присвоением адреса задачи паре регистров PCLATH:PCL, а задачи возвращают управление планировщику, совершая переход на него по GOTO. Таким образом экономится стек, т.к. ни задачи, ни планировщик не вызываются через CALL)&lt;/em&gt;. При выполнении программы возможна такая последовательность:
&lt;/p&gt;
&lt;table class=&quot;inline&quot;&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;th class=&quot;col0&quot;&gt; . &lt;/th&gt;&lt;th class=&quot;col1 centeralign&quot;&gt;  Действие  &lt;/th&gt;&lt;th class=&quot;col2 centeralign&quot;&gt;  Передача управления  &lt;/th&gt;&lt;th class=&quot;col3 centeralign&quot;&gt;  Стек  &lt;/th&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row1&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; 1 &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; Планировщик запускает задачу &lt;strong&gt;TaskA&lt;/strong&gt;  &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; PCLATH:PCL=TaskA &lt;/td&gt;&lt;td class=&quot;col3&quot;&gt; - &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row2&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; 2 &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Задача &lt;strong&gt;TaskA&lt;/strong&gt; вызывает функцию &lt;strong&gt;Delay10()&lt;/strong&gt;&lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; CALL Delay10. &lt;/td&gt;&lt;td class=&quot;col3&quot;&gt; &lt;strong&gt;ret_addr_A&lt;/strong&gt;&lt;br/&gt;
 - &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row3&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; 3 &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Функция &lt;strong&gt;Delay10&lt;/strong&gt; вызывает системный сервис &lt;strong&gt;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay&lt;/strong&gt;, который, инициализировав задержку, передает управление планировщику &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; GOTO sched &lt;/td&gt;&lt;td class=&quot;col3&quot;&gt; &lt;strong&gt;ret_addr_A&lt;/strong&gt;&lt;br/&gt;
 - &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row4&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; 4 &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Планировщик запускает задачу &lt;strong&gt;TaskB&lt;/strong&gt; &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; PCLATH:PCL=TaskB &lt;/td&gt;&lt;td class=&quot;col3&quot;&gt; &lt;strong&gt;ret_addr_A&lt;/strong&gt;&lt;br/&gt;
 - &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row5&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; 5 &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Задача &lt;strong&gt;TaskB&lt;/strong&gt; вызывает функцию &lt;strong&gt;Bsem_Wait()&lt;/strong&gt; &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; CALL Bsem_Wait &lt;/td&gt;&lt;td class=&quot;col3&quot;&gt; &lt;strong&gt;ret_addr_B&lt;/strong&gt;&lt;br/&gt;
 &lt;strong&gt;ret_addr_A&lt;/strong&gt;&lt;br/&gt;
 - &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row6&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; 6 &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Функция &lt;strong&gt;Bsem_Wait&lt;/strong&gt; вызывает системный сервис &lt;strong&gt;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Bsem_Wait&lt;/strong&gt;, который передает управление планировщику &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; GOTO sched &lt;/td&gt;&lt;td class=&quot;col3&quot;&gt; &lt;strong&gt;ret_addr_B&lt;/strong&gt;&lt;br/&gt;
 &lt;strong&gt;ret_addr_A&lt;/strong&gt;&lt;br/&gt;
 - &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row7&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; 7 &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Планировщик крутится вхолостую, пока идет задержка, запущенная в задаче &lt;strong&gt;TaskA&lt;/strong&gt; и пока не установлен семафор, которого ожидает задача &lt;strong&gt;TaskB&lt;/strong&gt; &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; &lt;/td&gt;&lt;td class=&quot;col3&quot;&gt;&lt;strong&gt;ret_addr_B&lt;/strong&gt;&lt;br/&gt;
 &lt;strong&gt;ret_addr_A&lt;/strong&gt;&lt;br/&gt;
 - &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row8&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; 8 &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Задержка закончилась, плнировщик передает управление задаче &lt;strong&gt;TaskA&lt;/strong&gt; в то же место, откуда был возврат в планировщик, а именно - в середину функиции &lt;strong&gt;Delay10&lt;/strong&gt; &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; PCLATH:PCL=Delay10 &lt;/td&gt;&lt;td class=&quot;col3&quot;&gt;&lt;strong&gt;ret_addr_B&lt;/strong&gt;&lt;br/&gt;
 &lt;strong&gt;ret_addr_A&lt;/strong&gt;&lt;br/&gt;
 - &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row9&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; 9 &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; И теперь - кульминация: функция делает возврат, при котором из стека берется последний положенный туда адрес, а именно - &lt;strong&gt;ret_addr_B&lt;/strong&gt; &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; RETURN &lt;/td&gt;&lt;td class=&quot;col3&quot;&gt; &lt;strong&gt;ret_addr_A&lt;/strong&gt;&lt;br/&gt;
 - &lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;

Как видно, после 9-ой операции мы из функции &lt;strong&gt;Delay10&lt;/strong&gt; вернемся в функцию-задачу &lt;strong&gt;TaskB&lt;/strong&gt;, хотя должны были вернуться в &lt;strong&gt;TaskA&lt;/strong&gt;.
&lt;/p&gt;

&lt;p&gt;

&lt;em&gt;&lt;strong&gt;Примечание.&lt;/strong&gt; В принципе, такой подход допускается, если у программиста есть уверенность в том, что в один момент времени только одна задача производит вызов такой функции. Однако, здесь надо быть крайне осторожным и хорошо понимать, что он делает. Поэтому, если Вы не уверены, что уследите за вызовами при дальнейшем росте программы, - не применяйте такой прием!&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#faq_по_осрв_osa&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Вернуться к списку FAQ&lt;/a&gt;&lt;/span&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Можно ли вызывать сервисы ожидания из функций, вызываемых задачами?&quot; [75201-81430] --&gt;
&lt;h3&gt;&lt;a name=&quot;ожидание_события_в_цикле&quot; id=&quot;ожидание_события_в_цикле&quot;&gt;Ожидание события в цикле&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Некоторые из присланных мне программ содержали однотипную ошибку, которую я бы хотел здесь обрисовать. Иногда бывает так, что в ходе ожидания какого-либо события требуется выполнять какое-то действие. Поэтому код этого ожидания некоторые писали без использования сервисов &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_xxx_Wait, заменяя их циклом do {…} while. Рассмотрим отвлеченный пример: пока ожидаем установки какого-то двоичного семафора, нам нужно сравнивать напряжения на двух входах АЦП и, в зависимости от результата сравнения, зажигать либо красный либо зеленый светодиод. 
&lt;/p&gt;

&lt;p&gt;
Код такого ожидания выглядел так:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;ADC_Read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;&amp;gt;&lt;/span&gt; ADC_Read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;    &lt;span class=&quot;co1&quot;&gt;// Сравниваем напряжения на двух аналоговых входах&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
            GREEN_LED &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
            RED_LED &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
            GREEN_LED &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
            RED_LED &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
        OS_Yield&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                       &lt;span class=&quot;co1&quot;&gt;// Возврат в планировщик&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_Bsem_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;BS_START&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Ошибка такого подхода заключается в том, что задача, крутясь в таком цикле, является &lt;strong&gt;всегда готовой&lt;/strong&gt; к выполнению. И если в программе есть задачи с более низким приоритетом, то они не смогут получить управление до тех пор, пока эта задача не дождется семафора. А если ожидаемый ей семафор должна установить как раз задача с более низким приоритетом, то программа просто зависнет в вечном ожидании. 
&lt;/p&gt;

&lt;p&gt;
Как быть в таких случаях? Здесь есть несколько вариантов решения этой коллизии.
&lt;/p&gt;

&lt;/div&gt;

&lt;h5&gt;&lt;a name=&quot;вынести_выполняемый_при_ожидании_код_в_отдельную_низкоприоритетную_задачу&quot; id=&quot;вынести_выполняемый_при_ожидании_код_в_отдельную_низкоприоритетную_задачу&quot;&gt;1. Вынести выполняемый при ожидании код в отдельную низкоприоритетную задачу&lt;/a&gt;&lt;/h5&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

С точки зрения концепции ОСРВ такой способ самый правильный. В данном конкретном примере сравнение напряжений на входах АЦП и ожидание семафора - функционально разные действия и нет никакого смысла выполнять их одновременно.
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;OST_TASK_POINTER tp;
&amp;nbsp;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Отдельная задача для работы с АЦП и светодиодами&lt;/span&gt;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task_ADC_Leds &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
   tp &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; OS_GetCurTask&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
   &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
   &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/* Здесь сравниваем напряжения */&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
&amp;nbsp;
        OS_Yield&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                       &lt;span class=&quot;co1&quot;&gt;// Возврат в планировщик&lt;/span&gt;
   &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Наша задача&lt;/span&gt;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;// Перед ожиданием создаем задачу сравнения напряжений&lt;/span&gt;
        OS_Task_Create&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;7&lt;/span&gt;, Task_ADC_Leds&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;// Ждем наш семафор&lt;/span&gt;
        OS_Bsem_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;BS_START&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;// Удаляем задачу сравнения напряжений&lt;/span&gt;
        OS_Task_Delete&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;tp&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Но при своей правильности этот подход не всегда оправдан, т.к. требует наличие свободного дескриптора на момент создания новой задачи, дополнительной глобальной переменной и времени на создание/удаление задачи.
&lt;/p&gt;

&lt;/div&gt;

&lt;h5&gt;&lt;a name=&quot;понизить_приоритет_на_время_ожидания&quot; id=&quot;понизить_приоритет_на_время_ожидания&quot;&gt;2. Понизить приоритет на время ожидания&lt;/a&gt;&lt;/h5&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Понизив приоритет до минимального, мы исключаем, что какая-либо задача окажется блокированной. С точки зрения ресурсов контроллера я бы назвал такой подход оптимальным.
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; prio;
    &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    prio &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; OS_Task_GetPriority&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;this_task&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;   &lt;span class=&quot;co1&quot;&gt;// Запоминаем текущий приоритет задачи&lt;/span&gt;
    OS_Task_SetPriority&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;this_task, &lt;span class=&quot;nu0&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;       &lt;span class=&quot;co1&quot;&gt;// Понижаем приоритет до минимального&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/* Здесь сравниваем напряжения */&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        OS_Yield&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_Bsem_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;BS_START&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    OS_Task_SetPriority&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;this_task, prio&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;    &lt;span class=&quot;co1&quot;&gt;// После цикла восстанавливаем сохраненный&lt;/span&gt;
                                             &lt;span class=&quot;co1&quot;&gt;// приоритет&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;em&gt;Примечание: рекомендуется понижать приоритет не до самого низкого (7-го), а до предпоследнего (6-го), т.к.низший приоритет удобно использовать для задачи SLEEP&amp;#039;а.&lt;/em&gt;
&lt;/p&gt;

&lt;/div&gt;

&lt;h5&gt;&lt;a name=&quot;вставить_в_цикл_небольшую_задержку&quot; id=&quot;вставить_в_цикл_небольшую_задержку&quot;&gt;3. Вставить в цикл небольшую задержку&lt;/a&gt;&lt;/h5&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Заменив &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Yield() на &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay(1), мы гарантировано на время одного системного тика ставим задачу в режим ожидания (время задержки можно увеличить, если одного тика мало):
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/* Здесь сравниваем напряжения */&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        OS_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_Bsem_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;BS_START&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Недостатком такого способа будет увеличение периода сравнения напряжений. Возможно, в данном примере это не страшно, но в другом случае, если операции внутри цикла критичны ко времени, это может отрицательно сказаться на логике работы устройства.
&lt;/p&gt;

&lt;p&gt;

(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#faq_по_осрв_osa&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Вернуться к списку FAQ&lt;/a&gt;&lt;/span&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Ожидание события в цикле&quot; [81431-88410] --&gt;
&lt;h3&gt;&lt;a name=&quot;можно_ли_создать_задачу_по_указателю_на_функцию&quot; id=&quot;можно_ли_создать_задачу_по_указателю_на_функцию&quot;&gt;Можно ли создать задачу по указателю на функцию?&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

&lt;strong&gt;Ответ: да&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
В OSA есть два недокументированных сервиса, которые позволяют это сделать. Я их не стал описывать в общей документации, чтобы не вносить путаницы в логику работы задач. Тем не менее, такой способ создания задач может оказаться очень удобным в пользовательских приложениях, где адрес функции-задачи привязан, например, к пункту меню. Предположим, у нас определен тип структуры, содержащей название пункта меню и адрес задачи, которую нужно будет выполнять по этому пункту:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;struct&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt; strMenu;
    &lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;  &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;Func&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; TMenuItem;&lt;/pre&gt;
&lt;p&gt;
Далее в программе определен массив этих структур:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;const&lt;/span&gt; TMenuItem UserMenu&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Load&amp;quot;&lt;/span&gt;, Task_Load&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;,
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Save&amp;quot;&lt;/span&gt;, Task_Save&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;,
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;View&amp;quot;&lt;/span&gt;, Task_View&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Edit&amp;quot;&lt;/span&gt;, Task_Edit&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Все эти задачи описываются как обычно, например:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task_Load &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Далее - одна тонкость. Чтобы компилятор правильно строил дерево вызовов подпрограмм, ему нужно указать, что функции, которые мы перечислили в массиве, являются задачами. Для этого в main() нужно для каждой такой функции вызвать сервис &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Task_Reserve:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; main &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    OS_Task_Reserve&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Task_Load&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Task_Reserve&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Task_Save&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Task_Reserve&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Task_View&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Task_Reserve&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Task_Edit&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;

После этого компилятор будет знать, что эти функции косвенно вызываются из main, и правильно распределит локальные переменные этих функций.
&lt;/p&gt;

&lt;p&gt;
Теперь в произвольном месте программы можно создавать задачи по указателю на функцию, пользуясь специальным сервисом &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Task_Create&lt;strong&gt;P&lt;/strong&gt;:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    LCD_Out&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;UserMenu&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;strMenu&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;        &lt;span class=&quot;co1&quot;&gt;// Выводим на экран название функции&lt;/span&gt;
    OS_Task_CreateP&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;, UserMenu&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;; &lt;span class=&quot;co1&quot;&gt;// Создаем задачу с высшим приоритетом&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;

(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#faq_по_осрв_osa&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Вернуться к списку FAQ&lt;/a&gt;&lt;/span&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Можно ли создать задачу по указателю на функцию?&quot; [88411-91384] --&gt;
&lt;h3&gt;&lt;a name=&quot;что_будет_если_отсылатьпринимать_сообщения_из_неинициализированной_очереди&quot; id=&quot;что_будет_если_отсылатьпринимать_сообщения_из_неинициализированной_очереди&quot;&gt;Что будет, если отсылать/принимать сообщения из неинициализированной очереди?&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

&lt;strong&gt;Ответ: ничего хорошего&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
Это относится не только к неинициализированной очереди, но и к неинициализированным: счетным семафорам, коротким сообщениям, указателям на сообщения. Неизвестно, что содержат в себе эти переменные на момент обращения к ним. Поэтому нужно всегда следить за тем, чтобы эти объекты ОС инициализировались &lt;strong&gt;до&lt;/strong&gt; первого обращения к ним.
&lt;/p&gt;

&lt;p&gt;
Рекомендую проанализировать работу следующего примера:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;OST_QUEUE q;
OST_MSG   smsg;
OST_MSG   rmsg;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task1 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    OS_Queue_Create&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;q&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;          &lt;span class=&quot;co1&quot;&gt;// Создаем очередь (инициализируем)&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        OS_Queue_Send&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;q, smsg&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;  &lt;span class=&quot;co1&quot;&gt;// Отсылаем сообщение в очередь&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task2 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        OS_Queue_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;q, rmsg&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;  &lt;span class=&quot;co1&quot;&gt;// Ожидаем сообщение из очереди&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; main &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    OS_Init&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Task_Create&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;, Task1&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Task_Create&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;, Task2&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Run&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Обратите внимание на расстановку приоритетов: приоритет задачи Task2 выше, чем Task1. Это означает, что первой выполнится именно задача Task2, т.е. та, которая ожидает сообщение из очереди, а задача, инициализирующая очередь, запустится второй (если &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Queue_Wait не вызовет сбой программы). При всей очевидности выхода из ситуации (а именно - правильной расстановке приоритетов), допустить такую ошибку очень просто. Для неприоритетного режима - вообще неизвестно, какая задача запустится первой. Поэтому очереди (и все остальные объекты, требующие инициализации) следует инициализировать так, чтобы на момент обращения к ним они гарантировано были инициализированны. Тривиальный способ - создавать их в функции main() до вызова сервиса &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Run().
&lt;/p&gt;

&lt;p&gt;

(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#faq_по_осрв_osa&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Вернуться к списку FAQ&lt;/a&gt;&lt;/span&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Что будет, если отсылать/принимать сообщения из неинициализированной очереди?&quot; [91385-94276] --&gt;
&lt;h3&gt;&lt;a name=&quot;изменение_типов_сообщений&quot; id=&quot;изменение_типов_сообщений&quot;&gt;Изменение типов сообщений&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

OSA имеет два вида сообщений: указатель на сообщение и короткое однобайтовое сообщение. Различаются они тем, что с помощью первого можно передавать любой объем информации, т.к. фактически передается только указатель на нее, а с помощью второго - только одно значение (по умолчанию это значения от 1 до 255). Учитывая архитектурные особенности PIC-контроллеров, программистам оставлена возможность изменять типы этих сообщений. 
&lt;/p&gt;

&lt;p&gt;
Сначала поговорим о типе &lt;strong&gt;указателя на сообщение&lt;/strong&gt;. По умолчанию указатель на сообщение имеет тип &lt;strong&gt;void* &lt;/strong&gt;, т.е. указатель на область RAM-памяти. Учитывая, что ядро PIC-контроллера построены по гарвардской архитектуре (раздельные шины адреса для памяти данных и программы), указатели на данные в ОЗУ и указатели на константы, хранящиеся в программной памяти, - это разные вещи. Поэтому ОСРВ OSA предоставляет программисту возможность на этапе написания программы выбрать тип указателей на сообщения. Этот тип нужно указать в файл конфигурации osacfg.h:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define OS_MSG_TYPE    const char *&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
в этом примере мы заменяем тип указателя на сообщение так, что сможем в программе обмениваться строковыми константами.

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;OST_MSG_CB  msg_cb;        &lt;span class=&quot;co1&quot;&gt;// Дескриптор сообщения&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt; MenuStrings&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Load&amp;quot;&lt;/span&gt;, &lt;span class=&quot;st0&quot;&gt;&amp;quot;Save&amp;quot;&lt;/span&gt;, &lt;span class=&quot;st0&quot;&gt;&amp;quot;Save as...&amp;quot;&lt;/span&gt;, &lt;span class=&quot;st0&quot;&gt;&amp;quot;Exit&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task_Menu &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; i &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;; i&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
            OS_Msg_Send&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;msg_cb, MenuString&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;em&gt;Примечание: в программе может быть применен только один тип для указателей на сообщения, и он не может меняться в ходе выполнения программы. (Исключение составляют указатели в HT-PICC18, когда указан ключ компиляции -CP24.)&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
Теперь об одной ошибке, вернее, о некоторой некорректности использования возможности замены типа указателя на сообщения. Несколько раз в программах видел такое:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;   name;
    &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;     age;
    &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;     weight;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; TMyStruct;
&amp;nbsp;
&lt;span class=&quot;co2&quot;&gt;#define OS_MSG_TYPE   TMyStruct *&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Т.е. программист создает некую свою структуру и тип сообщения заменяет указателем на нее. Ошибки здесь, конечно, нет. Но концептуально такой подход довольно спорный. За год программист может написать одну программу, а может и 10, и 20. Каждая программа может быть индивидуальна, и данные, которыми будут обмениваться задачи, - тоже. Если в каждой программе подменять тип указателя на сообщение каким-то специфичным указателем вместо void*, то возникнет некоторая неразбериха, да и проблемы с переносом модулей. Если предполагается работать с указателями на эти структуры, расположенные в RAM-области памяти, то лучше оставить тип void*. Это же замечание касается указателей на структуры, расположенные в ROM-области памяти - их лучше определять как &lt;strong&gt;const void* &lt;/strong&gt;.
&lt;/p&gt;

&lt;p&gt;

Теперь два слова о изменении типа &lt;strong&gt;короткого сообщения&lt;/strong&gt;. Для чего оно вообще сделано? Две причины: экономия RAM и повышение скорости. Довольно часто между программами нужно обмениваться незначительными объемами информации: &amp;quot;нажата кнопка 5&amp;quot;, &amp;quot;переключиться в режим 3&amp;quot;, &amp;quot;зажечь светодиод 12&amp;quot;. Преимущества перед указателями на сообщения:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; если бы мы использовали для передачи такой информации указатели на сообщения, нам бы пришлось тратить 2 байта ОЗУ (3 - для PIC18, 4 для PIC24 и dsPIC) на дескриптор сообщения и 1 байт на тело сообщения, куда будет указывать дескриптор, итого - 3 байта. При использовании короткого сообщения нам понадобится всего 1 байт. &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; при приеме указателя на сообщение программе потребуется сперва сформировать указатель в FSR, а только потом уже получить доступ к информации. При приеме же короткого сообщения мы получаем информацию напрямую.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; для указателей на сообщения нельзя менять содержимое отправленных данных, пока задача-приемник не получила сообщение. При использовании короткого сообщения это ограничение снимается, т.к. все сообщение помещается в дескриптор.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

По умолчанию короткое сообщение имеет тип &lt;strong&gt;unsigned char&lt;/strong&gt;. Этот тип может быть заменен на любой перечислимый тип (int, long, float, bit) заданием константы в файле osacfg.h:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define OS_SMSG_TYPE   unsigned long&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Такое, хоть и редко, но бывает нужно. 
&lt;/p&gt;

&lt;p&gt;
У короткого сообщения есть две особенности, которые нужно учитывать при проектировании программы: 
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; это сообщение может иметь только перечислимый тип (нужно учитывать при замене типа); т.е. этот тип нельзя переопределить на структуру, поскольку запись значения в короткое сообщение и чтение из него выполняется оператором присваивания ( = );&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; через него нельзя передавать нулевое значение. Этим приходится платить за использование такого эффективного инструмента. Нулевое значение рассматривается системой как отсутствие сообщения.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#faq_по_осрв_osa&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Вернуться к списку FAQ&lt;/a&gt;&lt;/span&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Изменение типов сообщений&quot; [94277-102370] --&gt;
&lt;h3&gt;&lt;a name=&quot;если_две_задачи_ожидают_одного_и_того_же_события&quot; id=&quot;если_две_задачи_ожидают_одного_и_того_же_события&quot;&gt;Если две задачи ожидают одного и того же события.&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

При написании программы с использованием ОСРВ OSA нужно учитывать одну особенность ее планировщика. При поиске лучшей готовой задачи для выполнения планировщик в цикле пробегается по всем дескрипторам, проверяет готовность задач и сравнивает их приоритеты. Управление получит готовая задача с высшим приоритетом. Если есть несколько задач с одинаковым приоритетом, то управление получит та, которая была рассмотрена планировщиком раньше. Дескрипторы задач хранятся в массиве, который планировщиком рассматривается как кольцевой. Каждый раз он начинает поиск готовой задачи со следующей после последней выполненной. Например, у нас 5 задач. Последней задачей выполнялась 3-я. Тогда порядок проверки задач при следующей работе планировщика будет таким: 4, 5, 1, 2, 3. 
&lt;/p&gt;

&lt;p&gt;
Проблема в том, что если две задачи ждут одного и того же события, например, семафора, который устанавливается третьей задачей, то одна из задач никогда не получит управление. Рассмотрим пример:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Задача, устанавливающая семафор&lt;/span&gt;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task1 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        OS_Bsem_Set&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;bsem&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        OS_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Первая задача, ожидающая семафор&lt;/span&gt;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task2 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        OS_Bsem_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;bsem&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Вторая задача, ожидающая семафор&lt;/span&gt;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task3 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        OS_Bsem_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;bsem&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; main &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    OS_Init&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    OS_Task_Create&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;, Task1&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;   &lt;span class=&quot;co1&quot;&gt;// Все задачи с равными приоритетами&lt;/span&gt;
    OS_Task_Create&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;, Task2&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Task_Create&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;, Task3&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    OS_Run&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Последовательность просмотра задач планировщиком будет такова: Task1, Task2, Task3, Task1, Task2, Task3, … . Проблема в том, что Task2 и Task3 не получают управления до тех пор, пока не будет установлен двоичный семафор, а устанавливается он только в Task1. Поэтому всегда будет происходить одна и та же последовательность: 
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Task1&lt;/strong&gt; устанавливает семафор &lt;strong&gt;bs&lt;/strong&gt;;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Task2&lt;/strong&gt; и &lt;strong&gt;Task3&lt;/strong&gt; становятся готовыми к выполнению;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; после работы &lt;strong&gt;Task1&lt;/strong&gt; планировщик начинает поиск задачи, начиная со следующей после последней выполненной, т.е. начиная с &lt;strong&gt;Task2&lt;/strong&gt;;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; планировщик рассматривает &lt;strong&gt;Task2&lt;/strong&gt;, видит, что она готова; лучший приоритет пока не выбран, поэтому лучшим на данный момент планировщик будет считать 1 (приоритет задачи Task2). Планировщик ее запоминает как кандидата на запуск;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; планировщик рассматривает &lt;strong&gt;Task3&lt;/strong&gt;, видит, что она готова, но ее приоритет не лучше, чем текущий лучший, поэтому он эту задачу пропускает;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; планировщик рассматривает &lt;strong&gt;Task1&lt;/strong&gt;, видит, что она не готова к выполнению (она в задержке);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; управление передается задаче &lt;strong&gt;Task2&lt;/strong&gt;;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Task2&lt;/strong&gt; сбрасывает семафор (сервис &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Bsem_Wait делает это автоматически), отрабатывает какие-то свои действия и возвращается в планировщик;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; планировщик начинает просмотр списка задач, начиная с &lt;strong&gt;Task3&lt;/strong&gt;, но уже видит, что семафор сброшен, поэтому &lt;strong&gt;Task3&lt;/strong&gt; опять ставится в ожидание;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; следующей управление получит &lt;strong&gt;Task1&lt;/strong&gt; по истечении времени задержки, а потом все начнется сначала.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Как тут быть? Единого способа решения нет. Можно ретранслировать семафор дальше, т.е. дождавшись его в Task2, сразу же установить его, чтобы и Task3 смогла получить управление. Но это не лучший вариант, т.к. неизвестно, сколько задач в цепочке, и на какой нужно останавливать установку семафора. Можно использовать счетный семафор, но опять же нужно знать, сколько задач его ожидают, чтобы установить в нем правильное число. На мой взгляд, самым удобным в таком случае будет использование флагов. В задаче-отправителе устанавливать все биты флага:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    OS_Flag_Set_1&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;flag, &lt;span class=&quot;nu12&quot;&gt;0xFF&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
а в задачах-приемниках ожидать только своего.

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;coMULTI&quot;&gt;/* В задаче Task2 */&lt;/span&gt;
    OS_Flag_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;flag, &lt;span class=&quot;nu12&quot;&gt;0x01&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Flag_Set_0&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;flag, &lt;span class=&quot;nu12&quot;&gt;0x01&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;coMULTI&quot;&gt;/* В задаче Task3 */&lt;/span&gt;
    OS_Flag_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;flag, &lt;span class=&quot;nu12&quot;&gt;0x02&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Flag_Set_0&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;flag, &lt;span class=&quot;nu12&quot;&gt;0x02&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
В общем, способы решения есть, а какой применять, - на усмотрение программиста. &lt;strong&gt;Главное - надо помнить об этой особенности планировщика.&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;

(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#faq_по_осрв_osa&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Вернуться к списку FAQ&lt;/a&gt;&lt;/span&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Если две задачи ожидают одного и того же события.&quot; [102371-109781] --&gt;
&lt;h3&gt;&lt;a name=&quot;модификация_тела_сообщения_до_того_как_оно_будет_принято&quot; id=&quot;модификация_тела_сообщения_до_того_как_оно_будет_принято&quot;&gt;Модификация тела сообщения до того, как оно будет принято&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

В одной присланной мне программе была допущена такая ошибка: задача формировала тело сообщения, отправляла другой задаче указатель на него, а потом сразу же модифицировала для отправки следующего. Выглядело это так:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;OST_MSG_CB msg_cb;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
       &lt;span class=&quot;co1&quot;&gt;// Формируем первое сообщение&lt;/span&gt;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'1'&lt;/span&gt;;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'2'&lt;/span&gt;;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'3'&lt;/span&gt;;
       OS_Msg_Send&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;msg_cb, buf&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
       OS_Yield&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
       &lt;span class=&quot;co1&quot;&gt;// Формируем второе сообщение&lt;/span&gt;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'5'&lt;/span&gt;;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'6'&lt;/span&gt;;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'7'&lt;/span&gt;;
       OS_Msg_Send&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;msg_cb, buf&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
       OS_Yield&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
       &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
   &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Не смотря на то, что после отправки сообщения выполняется &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Yield, чтобы дать возможность адресату получить сообщение, оно может и не доставиться с первого раза (по любой причине: задача-приемник чем-то занята, или имеет низкий приоритет, или, наконец, находится в режиме паузы). Сервис &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Msg_Send устроен так, что он не сможет отправить сообщение до тех пор, пока предыдущее не получено адресатом. Тем не менее, следует помнить, что этот сервис следит только за дескриптором сообщения, а не за областью памяти, где фактически расположено тело. 
&lt;/p&gt;

&lt;p&gt;
Что произойдет в нашем примере, если после первого &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Yield задача-адресат не успеет принять отправленное ей сообщение? Фактически ей отправляется только адрес области памяти, где располагается массив buf, в котором на момент отправки лежат значения &amp;#039;1&amp;#039;, &amp;#039;2&amp;#039; и &amp;#039;3&amp;#039;. Итак, после выполнения &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Yield сообщение не было принято. Планировщик возвращает управление задаче отправителю, и она продолжает свое выполнение с того места, откуда вышла, т.е. со следующей строки после &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Yield. Здесь у нас происходит замена элементов буфера buf на &amp;#039;5&amp;#039;, &amp;#039;6&amp;#039; и &amp;#039;7&amp;#039;. После этого задача Task пытается отправить очередное сообщение, но так как предыдущее еще не получено адресатом, то эта задача становится в ожидание, когда дескриптор освободится. 
&lt;/p&gt;

&lt;p&gt;
По прошествии какого-то времени адресат получит сообщение и освободит дескриптор, но в теле сообщения будут уже новые значения (не &amp;quot;123&amp;quot;, а &amp;quot;567&amp;quot;). Задача отправитель же, дождавшись освобождения дескриптора, отсылает следующее сообщение; фактически - это тот же указатель на тот же участок памяти. И задача-адресат второй раз подряд получит &amp;quot;567&amp;quot;.
&lt;/p&gt;

&lt;p&gt;
Как этого избежать? Всего-навсего перед формированием нового сообщения в том же буфере нужно проверять, было ли предыдущее сообщение доставлено.

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;       &lt;span class=&quot;co1&quot;&gt;// Формируем первое сообщение&lt;/span&gt;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'1'&lt;/span&gt;;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'2'&lt;/span&gt;;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'3'&lt;/span&gt;;
       OS_Msg_Send&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;msg_cb, buf&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
       OS_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_Msg_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;msg_cb&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;        &lt;span class=&quot;co1&quot;&gt;// Ставим задачу в режим ожидания до тех пор,&lt;/span&gt;
                                              &lt;span class=&quot;co1&quot;&gt;// пока сообщение не будет принято&lt;/span&gt;
&amp;nbsp;
       &lt;span class=&quot;co1&quot;&gt;// Формируем второе сообщение&lt;/span&gt;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'5'&lt;/span&gt;;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'6'&lt;/span&gt;;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'7'&lt;/span&gt;;
       OS_Msg_Send&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;msg_cb, buf&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
       OS_Yield&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;

(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#faq_по_осрв_osa&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Вернуться к списку FAQ&lt;/a&gt;&lt;/span&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Модификация тела сообщения до того, как оно будет принято&quot; [109782-114615] --&gt;
&lt;h3&gt;&lt;a name=&quot;использование_таймеров_вне_задач&quot; id=&quot;использование_таймеров_вне_задач&quot;&gt;Использование таймеров вне задач&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Как статические, так и динамические таймеры могут использоваться в любом месте программы (за исключением сервисов Wait и Delay). Это позволяет:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;1. В любом месте программы ожидать какого-то условия с выходом по таймауту.&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; MyFunc1 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    OS_Stimer_Run&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                  &lt;span class=&quot;co1&quot;&gt;// Запускаем статический таймер 0&lt;/span&gt;
                                           &lt;span class=&quot;co1&quot;&gt;// на отсчет 10 системных тиков&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;RB0 &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_Stimer_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;    &lt;span class=&quot;co1&quot;&gt;// Ожидаем установки RB0&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;continue&lt;/span&gt;;
    &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;

&lt;strong&gt;2. Формировать задержку внутри фоновых функций (не задач).&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; MyFunc2 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    OS_Stimer_Run&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                  &lt;span class=&quot;co1&quot;&gt;// Запускаем статический таймер 0&lt;/span&gt;
                                           &lt;span class=&quot;co1&quot;&gt;// на отсчет 10 системных тиков&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_Stimer_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;continue&lt;/span&gt;;  &lt;span class=&quot;co1&quot;&gt;// Ожидаем конца счета&lt;/span&gt;
    &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;strong&gt;3. Выделять квант времени для работы какой-либо функции.&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; buffer&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; OST_DTIMER dt;
&amp;nbsp;
    OS_Dtimer_Create&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;dt&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                   &lt;span class=&quot;co1&quot;&gt;// Инициализируем таймер&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        OS_Dtimer_Run&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;dt, &lt;span class=&quot;nu0&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;              &lt;span class=&quot;co1&quot;&gt;// Выделяем для функции Receive квант&lt;/span&gt;
                                            &lt;span class=&quot;co1&quot;&gt;// времени в 50 тиков&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Receive&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;dt&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
            &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; Receive &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;OST_DTIMER &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;dt&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; bit b;
    &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        b &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; GetBit&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                       &lt;span class=&quot;co1&quot;&gt;// Принимаем бит данных&lt;/span&gt;
        ShiftBit&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;b&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                        &lt;span class=&quot;co1&quot;&gt;// Вдвигаем его в буфер&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;CheckSumOK&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;         &lt;span class=&quot;co1&quot;&gt;// Проверяем контрольную сумму&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_Dtimer_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;dt&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;        &lt;span class=&quot;co1&quot;&gt;// Висим в цикле, пока таймер не досчитает&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;co1&quot;&gt;// Попали сюда, значит ничего не приняли за отведенное время&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#faq_по_осрв_osa&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Вернуться к списку FAQ&lt;/a&gt;&lt;/span&gt;)
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Использование таймеров вне задач&quot; [114616-117223] --&gt;
&lt;h3&gt;&lt;a name=&quot;почему_виснет_os_delay&quot; id=&quot;почему_виснет_os_delay&quot;&gt;Почему виснет OS_Delay?&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

&amp;quot;Почему после вызова &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay() задача подвисает, значение таймеров не изменяется, хотя в osacfg.h определена константа &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_ENABLE_TTIMERS?&amp;quot;. Как ни странно, вопрос довольно частный.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Ответ: счет таймеров выполняет сервис &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer, который должен периодически вызываться.&lt;/strong&gt;
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Почему виснет OS_Delay?&quot; [117224-117739] --&gt;
&lt;h2&gt;&lt;a name=&quot;советы_по_оптимизации&quot; id=&quot;советы_по_оптимизации&quot;&gt;Советы по оптимизации&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Здесь будут описаны правила, советы и рекомендации по конфигурированию OSA, а также по применению сервисов OSA с целью увеличения эффективности использования ресурсов микроконтроллера. Есть три ресурса, которые хотелось бы сэкономить: RAM, ROM и время. Сначала я предполагал разбить этот раздел на три части, посвятив каждую из них своему параметру. Однако, получилось так, что приемы и советы по экономии RAM и ROM практически идентичны, поэтому им будет посвящена одна часть.
&lt;/p&gt;

&lt;p&gt;
&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#оптимизация_памяти&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Оптимизация памяти&lt;/a&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#отключение_приоритетов&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Отключение приоритетов&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#размерность_таймеров&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Размерность таймеров&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#замена_таймеров_задач_статическими_таймерами&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Замена таймеров задач статическими таймерами&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#os_timer_inline&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;OS_Timer inline&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#размещение_переменных&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Размещение переменных&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#отправка_сообщений&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Отправка сообщений&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#очереди_сообщений&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Очереди сообщений&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#оптимизация_скорости&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Оптимизация скорости&lt;/a&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#выбор_приоритетов&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Выбор приоритетов&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#поное_отключение_приоритетов&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Отключение приоритетов&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#замена_ожидания_на_задержку&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Замена ожидания на задержку&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#проверка_условия_перед_ожиданием&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Проверка условия перед ожиданием&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Советы по оптимизации&quot; [117740-120206] --&gt;
&lt;h3&gt;&lt;a name=&quot;оптимизация_памяти&quot; id=&quot;оптимизация_памяти&quot;&gt;Оптимизация памяти&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Оптимизация памяти в основном производится за счет правильного конфигурирования, но есть и приемы, относящиеся к использованию сервисов. Начальная конфигурация по умолчанию (когда файл osacfg.h пустой) подобрана так, чтобы обеспечить программиста всем необходимым, не давая ничего лишнего. Но каждая программа индивидуальна и есть смысл для каждой подбирать оптимальную конфигурацию.
&lt;/p&gt;

&lt;p&gt;
(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;&lt;/span&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;отключение_приоритетов&quot; id=&quot;отключение_приоритетов&quot;&gt;Отключение приоритетов&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Во многих приложениях отключение приоритетности - очень полезная вещь. Как было описано выше (в разделе &lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#как_расставлять_приоритеты&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Как расставлять приоритеты&lt;/a&gt;&lt;/span&gt;), в большинстве приложений под кооперативной ОСРВ приоритетность не нужна. Когда мы отключаем приоритетность, код планировщика сильно упрощается и сокращается, т.к. он больше не содержит цикла предварительного перебора всех задач в поиске готовых и кода сравнения приоритетов. Кроме того, из прораммы выкидываются системные переменные, содержащие максимальный приоритет, адрес задачи с максимальным приоритетом и адрес последней выполненной задачи. Т.е., отключив приоритеты, мы экономим и RAM, и ROM, и время работы планировщика (см. &lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#отключение_приоритетов&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;ниже&lt;/a&gt;&lt;/span&gt;).
&lt;/p&gt;

&lt;p&gt;
По умолчанию приоритетность включена. Отключить ее можно, задав в файле osacfg.h константу:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define OS_DISABLE_PRIORITY&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;&lt;/span&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;размерность_таймеров&quot; id=&quot;размерность_таймеров&quot;&gt;Размерность таймеров&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Таймеры в OSA - очень гибкий в настройке инструмент. Помимо того, что есть три типа таймеров: таймеры задач, пользовательские статические и пользовательские динамические таймеры, - каждый из которых обладает своими преимуществами, есть еще возможность выбирать размерность каждого типа таймера в зависимости от возложенных на него задач.
&lt;/p&gt;

&lt;p&gt;
&lt;em&gt;(&lt;strong&gt;Примечание.&lt;/strong&gt; Есть еще четвертый тип таймеров, исторически сложилось так, что называется он &amp;quot;статические таймеры старого типа&amp;quot;, но т.к. эти таймеры были довольно сложны в настройке, от их дальнейшего сопровождения пришлось отказаться, хотя в системе они остались и функционируют. См. приложение к документации)&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
По умолчанию размерность таймера равна двум байтам. Это покрывает большую часть потребностей в организации задержек в программах. Такие таймеры могут отсчитывать до 65535 системных тиков (периодов вызова &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer). Больше обычно не нужно. Но зато часто встречаются случаи, когда все задержки (и ожидания событий с таймаутами) не длиннее 255 тиков. Например, &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer вызывается в обработчике прерывания по TMR2, настроенного так, чтобы его период был 10 мс. В этом случае, используя отсчет таймера в 255 тиков, мы получаем задержку в 2.5 секунды. Для многих приложений этого более чем достаточно. 
&lt;/p&gt;

&lt;p&gt;
Поэтому во многих случаях есть смысл таймеры задач сделать однобайтовыми. Делается это заданием размерности таймера в файле osacfg.h:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define OS_TTIMER_SIZE      1&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Что мы выигрываем, уменьшив размерность таймеров задач? 
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Во-первых, функция инициализации таймера в своих параметрах будет получать не 2 байта, а один (кто знаком со внутренней организацией HT-PICC, знают, что однобайтовый параметр передается напрямую через WREG, в то время как двухбайтовый требует для передачи дополнительную ячейку памяти);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Во-вторых, сильно сокращается код функции &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer(). Дело в том, что эта функция для сокращения размера кода и скорости работы увеличивает все таймеры не в цикле, а по очереди напрямую. Поэтому уменьшение размерности таймера задач уменьшит размер функции пропорционально количеству задач (чем больше задач, тем сильнее сократится &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; В-третьих, все дескрипторы задач станут на один байт меньше (при 10 задачах в системе уже экономия в 10 байт, а для PIC16 это немало).&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;em&gt;&lt;strong&gt;Примечание 1.&lt;/strong&gt; Размерность можно также менять и у статических, и у динамических таймеров.&lt;/em&gt;&lt;br/&gt;
 
&lt;em&gt;&lt;strong&gt;Примечание 2.&lt;/strong&gt; Для 16-разрядных контроллеров (PIC24 и dsPIC) уменьшение размерности таймера до одного байта  не приведет к сокращению кода или экономии RAM.&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;&lt;/span&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;замена_таймеров_задач_статическими_таймерами&quot; id=&quot;замена_таймеров_задач_статическими_таймерами&quot;&gt;Замена таймеров задач статическими таймерами&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Иногда при дефиците ресурсов может оказаться полезным воспользоваться статическими таймерами, вместо таймеров задач. При включении таймеров задач (включаются определением константы &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_ENABLE_TTIMERS) появляется возможность использовать в задачах задержки (&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay) и ожидание событий с выходом по таймауту (&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_xxx_Wait_TO). За каждым дескриптором задачи при этом закрепляется свой таймер (так называемый таймер задач), и их будет столько, сколько дескрипторов зарезервировано в osacfg.h (константа &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_TASKS). Получается, что если у нас 10 задач, а таймеры используются, например, только в трех, то мы имеем в памяти 7 переменных, которые не используются, но занимают место. 
&lt;/p&gt;

&lt;p&gt;
В таких случаях имеет смысл отключить таймеры задач и воспользоваться статическими таймерами. Для нашего примера мы определяем константу в osacfg.h:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define OS_STIMERS     3&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
И теперь у нас только 3 ячейки памяти занято таймерами, а мы сэкономили 7 ячеек. При двухбайтовых таймерах это 14 байт оперативной памяти. Теперь задержки в задачах будут выглядеть так:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    OS_Stimer_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;ST_ID, &lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;

где ST_ID - номер статического таймера (от 0 до 2 в нашем примере).
&lt;/p&gt;

&lt;p&gt;
Как заменить сервисы ожидания с выходом по таймауту, если все сервисы &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_xxx_Wait_TO требуют наличия таймеров задач? Очень просто, воспользовавшись сервисом &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Cond_Wait, который позволяет ожидать в задаче &lt;strong&gt;любое&lt;/strong&gt; условие. Например, мы ждем сообщение из очереди:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;co1&quot;&gt;// С таймерами задач&lt;/span&gt;
    OS_Queue_Wait_TO&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;queue, msg, &lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_IsTimeout&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/* обрабатываем сообщение */&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;co1&quot;&gt;// Без таймеров задач, но со статическими таймерами&lt;/span&gt;
    OS_Stimer_Run&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Cond_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;OS_Queue_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;queue&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_Stimer_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_Stimer_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        OS_Queue_Accept&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;queue, msg&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;coMULTI&quot;&gt;/* обрабатываем сообщение */&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
При использовании статического таймера запись оказалась более громоздкой, тем не менее сгенерированный код будет примерно одинаковым по размеру.
&lt;/p&gt;

&lt;p&gt;
Итак, что мы выигрываем от замены таймеров задач статическими таймерами?
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; В памяти не создаются таймеры для тех задач, которые их не используют;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Сокращается обработчик &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer (т.к. он должен пробегаться по всему списку таймеров задач, даже по неиспользуемым);&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Теряем мы удобство и наглядность.
&lt;/p&gt;

&lt;p&gt;
Еще одно применение статических таймеров можно рассмотреть в контексте предыдущего параграфа (&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#размерность_таймеров&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Размерность таймеров&lt;/a&gt;&lt;/span&gt;). Там мы указали, что часто бывает так, что задержки в задачах не превышают 255 системных тиков. Но бывает и так, что в 9-ти задачах не превышают, а 10-ой, хоть тресни, нужен 32-разрядный таймер. Понятно, что из-за одной задачи не хотелось бы всем приделывать 4-байтовые таймеры. Поэтому удобно организовать один 4-байтовый статический таймер для этой задачи, а всем остальным определить 1-байтовые. Вот фрагмент файла osacfg.h:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define OS_ENABLE_TTIMERS          // Разрешаем использование таймеров задач&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define OS_TTIMER_SIZE      1      // Размерность таймеров задач&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define OS_STIMERS          1      // Определяем один статический таймер&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define OS_STIMER_SIZE      4      // Размерность статического таймера&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;&lt;/span&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;os_timer_inline&quot; id=&quot;os_timer_inline&quot;&gt;OS_Timer inline&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Сервис &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer вызывает системную функции _&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer(). Учитывая, что чаще всего вызов этого сервиса производят по переполнению какого-либо таймера (TMR0, TMR1, TMR2), обычно он располагается внутри функции-прерывания. Компиляторы от HTSoft в таком случае ведут себя следующим образом: т.к. сама функция _&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer() относительно функции прерывания находится во внешнем другом модуле, то функция прерывания ничего не знает о ресурсах, которые использует _&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer(), поэтому при входе в прерывание сохраняются все критичные регистры, которые, по предположению компилятора, может изменить внешняя функция. К ним относятся: все FSR, пара PRODH:PROLH, 15 регистров btemp, и тройка регистров TABLAT, TBLPTRH:TBLPTRL. Возможно в самом прерывании они и не используются, а все прерывание выглядит так:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; interrupt myisr &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    OS_EnterInt&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;TMR2IF&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        TMR2IF &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
        OS_Timer&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
    OS_LeaveInt&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Но на этапе компиляции файла, содержащего код прерывания, компилятор не знает, что творится внутри функции, вызываемой сервисом &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer. Сохранение/восстановление при входе/выходе из прерывания занимает более 100 слов памяти ROM и, соответственно, более 100 машинных циклов. Т.е. если у нас таймер запрограммирован так, чтобы прерывание происходило каждые 256 циклов, то более 40% всего процессорного времени будет тратиться только на вход и выход из прерывания. 
&lt;/p&gt;

&lt;p&gt;
В ОСРВ OSA есть возможность вместо вызова функции _&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer подставлять ее тело напрямую. Для этого нужно определить константу в файле osacfg.h:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define OS_USE_INLINE_TIMER&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;strong&gt;Ограничение:&lt;/strong&gt; если эта константа определена, то в программе &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer() может вызываться в единственном месте. Возможно, в дальнейшем это ограничение будет снято.
&lt;/p&gt;

&lt;p&gt;
(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;&lt;/span&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;размещение_переменных&quot; id=&quot;размещение_переменных&quot;&gt;Размещение переменных&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

(Этот параграф имеет отношение не столько к OSA, сколько к PIC18)
&lt;/p&gt;

&lt;p&gt;
Для PIC18 некоторый выигрыш по программной памяти даст размещение переменных в ACCESS-банке (первые 128 или 96 байт RAM-памяти). Обращение к таким переменным производится без предварительной установки регистра BSR, следовательно, при частом обращении к ним можно сэкономить на всех инструкциях movlb. Свои переменные можно размещать в этом банке вручную. Например, для HT-PICC18:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;near &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; a;
near &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;  i;&lt;/pre&gt;
&lt;p&gt;

для MCC18:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#pragma udata access my_vars&lt;/span&gt;
near &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; a;
near &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;  i;&lt;/pre&gt;
&lt;p&gt;
В ОСРВ OSA также есть возможность размещать все внутренние переменные в разных банках: дескрипторы задач, системные переменные, статический таймеры, бинарные семафоры. Для каждого типа этих данных можно указать свой банк (0 - access, 1 - остальная память):

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define OS_BANK_OS          0         // Размещение системных переменных&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define OS_BANK_TASKS       1         // Размещение дескрипторов задач&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define OS_BANK_BSEMS       0         // Размещение двоичных семафоров&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define OS_BANK_OS          1         // Размещение статических таймеров&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;&lt;/span&gt;)
&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;отправка_сообщений&quot; id=&quot;отправка_сообщений&quot;&gt;Отправка сообщений&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Обычно сообщения отправляются сервисом &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Msg_Send. Как этот сервис работает? Предварительно он проверяет, занят дескриптор сообщения или свободен (т.е. обработано ли предыдущее сообщение или еще нет). Если дескриптор занят, то задача становится в режим ожидания и передает управление планировщику. Иногда бывает так, что не принципиально, обработалось предыдущее сообщение или нет. Например, задача измеряет напряжение бортовой сети автомобиля и сообщением отправляет его головной задаче. Головной задаче не важна последовательность изменений напряжения, ей нужно текущее значение. Поэтому иногда нет смысла выполнять лишние операции по проверки занятости дескриптора, по формированию адреса возврата, да еще задержки на неопределенное время. Для отправки сообщения, не дожидаясь освобождения дескриптора (т.е. фактически для перетирания старого тела сообщения новым), можно пользоваться сервисом:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    OS_Msg_Send_Now&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;msg_cb, msg&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Например, для PIC16 этот сервис занимает 7 слов программной памяти, в то время как &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Msg_Send требует 17 слов.
&lt;/p&gt;

&lt;p&gt;
&lt;em&gt;&lt;strong&gt;Примечание&lt;/strong&gt;. Это же относится к отправке коротких сообщений, а также к отправке сообщений и коротких сообщений в очередь.&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;&lt;/span&gt;)
&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;очереди_сообщений&quot; id=&quot;очереди_сообщений&quot;&gt;Очереди сообщений&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

OSA предоставляет возможность программисту использовать два типа очередей в своей программе, причем их можно использовать одновременно, т.к. они независимы друг от друга. Однако, их независимость порождает небольшую проблему: в программе присутствуют отдельные функции по добавлению/извлечению сообщения для обоих типов очередей. Т.е. в программной памяти два экземпляра функции _&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Queue_Send и два экземпляра функции _&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Queue_Accept. Это позволяет программисту использовать в своей программе сообщения разных типов и размерностей. Но если в программе используются сообщения одинаковых размерностей? Т.е. размерность указателя на сообщения такая же, как размерность короткого сообщения (для PIC16, например, указатель на RAM однобайтовый). В этом случае использование двух экземпляров функций неоправданно. 
&lt;/p&gt;

&lt;p&gt;
В таких случаях программисту предоставляется возможность объединить эти функции, т.е., вернее, использовать одни и те же функции для сообщений разных типов. Для этого нужно в файле osacfg.h определить константу:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define OS_QUEUE_SQUEUE_IDENTICAL&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Так мы сэкономим около 100 слов программной памяти. Однако после применения такого приема следует помнить, что был сделан допуск относительно размерности сообщений. И если вдруг нужно будет поменять тип одного из сообщений (указателя на сообщение или короткого сообщения), то следует пересмотреть возможность использования одной функции для работы с очередями этих сообщений.
&lt;/p&gt;

&lt;p&gt;
(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;&lt;/span&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Оптимизация памяти&quot; [120207-143117] --&gt;
&lt;h3&gt;&lt;a name=&quot;оптимизация_скорости&quot; id=&quot;оптимизация_скорости&quot;&gt;Оптимизация скорости&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;выбор_приоритетов&quot; id=&quot;выбор_приоритетов&quot;&gt;Выбор приоритетов&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Давайте еще раз рассмотрим принцип работы планировщика в ОСРВ OSA. Планировщик по очереди перебирает все активные (созданные) задачи, проверяя их готовность. Одновременно он сравнивает приоритеты у всех готовых задач, для чего лучший из приоритетов, а также адрес задачи с лучшим приоритетам сохраняет в своих внутренних переменных. После прохода всех задач управление передается той, чей адрес сохранен во внутренней переменной, т.е. чей приоритет был высшим из всех готовых задач.
&lt;/p&gt;

&lt;p&gt;
Если во время проверки встречаются несколько готовых задач с одинаковым приоритетом, то управление получит та из них, которая была проверена первой. Получается так, что если планировщик натыкается на готовую задачу с высшим приоритетом (0), то дальнейший поиск делать бессмысленно, поскольку, даже если и найдутся еще готовые задачи с высшим приоритетом, планировщик все равно запустит ту, которая была найдена первой. Планировщик OSA пользуется этим моментом и, находя готовую задачу с высшим приоритетом, сразу же прерывает поиск и передает управление найденной задаче.
&lt;/p&gt;

&lt;p&gt;
Учитывая, что полное время проверки готовности одной задачи (с выбором адреса задачи, с передачей ей управления для проверки готовности, с самой проверкой и с возвратом в планировщик) длится около 70 тактов. И если у нас 10 задач, то общий поиск будет длиться около 700 тактов.
Если же присутствует задача с нулевым приоритетом, то в случае ее готовности время работы планировщика сократится в среднем вдвое (это время будет колебаться от 70 до 700 тактов в зависимости от того, с какой задачи планировщик начинает просмотр).
&lt;/p&gt;

&lt;p&gt;
Итак, рекомендация такова: если используется приоритетный режим, то желательно самым приоритетным задачам устанавливать именно высший (а не просто самый высокий) приоритет, т.к. это ускорит работу планировщика.
&lt;/p&gt;

&lt;p&gt;
(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;&lt;/span&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;полное_отключение_приоритетов&quot; id=&quot;полное_отключение_приоритетов&quot;&gt;Полное отключение приоритетов&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Об этом уже разговор был. Постольку, поскольку при отключении приоритетности убирается код, занимающийся предварительным перебором задач и сравнением приоритетов, то работа планировщика ускорится в &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_TASKS раз.
&lt;/p&gt;

&lt;p&gt;
(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;&lt;/span&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;замена_ожидания_на_задержку&quot; id=&quot;замена_ожидания_на_задержку&quot;&gt;Замена ожидания на задержку&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Два слова о том, как проверяется условие при ожидании. Например, задача ожидает установку двоичного семафора. Что при этом происходит:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; задача переводит себя в режим ожидания (сбрасывает себе флаг bReady);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; задача сохраняет текущее значение программного счетчика;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; задача выполняет проверку семафора;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; если семафор установлен, то задача переводит себя в режим готовности (устанавливает флаг bReady);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; задача возвращает управление планировщику; (обратите внимание, что возврат в планировщик произойдет при любом значении семафора, т.к. среди задач может оказаться более приоритетная, которая ожидает тот же семафор);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; планировщик, проверив все остальные задачи, доходит до нашей;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; планировщик передает управление на тот адрес, который задача сохранила в шаге 2 (при этом мы попадаем в задачу как раз в то место, где производится проверка семафора);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; задача выполняет повторную проверку семафора (он за это время мог быть сброшен более приоритетной задачей);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; если семафор уже сброшен, то задача переводит себя в режим ожидания (сбрасывает флаг bReady);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; если семафор установлен и задача готова к выполнению, то она идет выполнять дальнейший код (который следует за ожиданием семафора);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; если семафор установлен, но задача еще в режиме ожидания, то идем на шаг 4.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Т.е. &lt;strong&gt;при ожидании события планировщик всегда заглядывает внутрь задачи&lt;/strong&gt; (передает ей управление, чтобы она сама проверила свое условие). Это требует времени. Если семафор устанавливается довольно редко, а скорость реакции на него некритична (например, в будильнике, по которому мы просыпаемся на работу не так важно, зазвенит он в 07:00:00 или в 07:00:05), то очень много тактов процессора тратится впустую.
&lt;/p&gt;

&lt;p&gt;
Теперь рассмотрим поведение планировщика, когда какая-либо задача находится в ожидании задержки &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay. Что происходит, когда задача выполняет &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay(100):
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; таймер в дескрипторе задачи инициализируется указанным значением (в нашем примере 100);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; задача устанавливает себе бит bDelay (что говорит за то, что она выполняет задержку);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; задача запоминает текущее значение программного счетчика;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; задача возвращает управление планировщику;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; когда у планировщика доходит очередь до проверки этой задачи, он предварительно проверяет бит bDelay  в ее дескрипторе;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; если этот бит установлен, то понятно, что задача еще не готова, и планировщик ее пропускает.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

&lt;em&gt;(Примечание. Бит bDelay сбрасывается обработчиком сервиса &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer).&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
Т.е. при выполнении задачей задержки &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay &lt;strong&gt;планировщик не передает управление задаче до конца задержки&lt;/strong&gt;. Т.е. не теряет 50 тактов на заход внутрь задачи и проверку условия.
&lt;/p&gt;

&lt;p&gt;
Таким образом, если есть некритичные к реакции события, можно ускорить работу планировщика, делая замены подобные этой:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;co1&quot;&gt;// Обычное ожидание события&lt;/span&gt;
    OS_Bsem_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;co1&quot;&gt;// Ожидание, которое ускорит работу планировщика&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        OS_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;              &lt;span class=&quot;co1&quot;&gt;// Проверяем условие с интервалом в 10 системных тиков&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_Check_Bsem&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;

&lt;em&gt;&lt;strong&gt;Примечание.&lt;/strong&gt; Эффективной будет только задержка с использованием таймеров задач. Статический и динамические таймеры здесь не подойдут.&lt;/em&gt;(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;&lt;/span&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;проверка_условия_перед_ожиданием&quot; id=&quot;проверка_условия_перед_ожиданием&quot;&gt;Проверка условия перед ожиданием&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Вернемся к описанию работы планировщика при ожидании события в какой-либо задаче, описанному в предыдущем параграфе. Как видно, вне зависимости от того, произошло событие на момент проверки или нет, мы по-любому передаем управление планировщику. Объяснение такому поведению уже приводилось: среди задач может оказаться более приоритетная, ожидающая этого же события, и управление должно быть сперва передано ей. Но допустим, что мы на 100% уверены, что второй задачи, ожидающей того же события нет. А нам жалко терять время на выход из задачи и на ожидание того, когда она станет самой приоритетной. Поэтому в таком случае можно прибегнуть к следующему приему:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_Bsem_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; OS_Bsem_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Т.е. мы сперва проверяем, не установлен ли уже семафор, и, только убедившись, что еще нет, возвращаемся в планировщик.
&lt;/p&gt;

&lt;p&gt;
(&lt;span class=&quot;curid&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;&lt;/span&gt;)
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Оптимизация скорости&quot; [143118-154579] --&gt;
&lt;h2&gt;&lt;a name=&quot;заключение&quot; id=&quot;заключение&quot;&gt;Заключение&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Итак, в статье были рассмотрены рекомендации по принятию решения, использовать ли ОСРВ в своих проектах или нет, рассмотрены основные рекомендации и советы по написанию более эффективных программ с использованием ОСРВ OSA, приведены ответы на часто задаваемые вопросы с объяснениями и примерами, а также произведен некий &amp;quot;взгляд изнутри&amp;quot; операционной системы OSA для более глубокого понимания функционирования планировщика и сервисов. 
&lt;/p&gt;

&lt;p&gt;
Возможно, в этой статье я не коснулся каких-то важных моментов, которые нужно было рассмотреть, или Вы, читая статью, не нашли ответ на свой вопрос, или из-за местами непонятного изложения появился какой-то новый вопрос. Может, есть какие-то замечания или предложения, - не стесняйтесь, пишите на мейл osa@pic24.ru или testerplus@mail.ru.
&lt;/p&gt;

&lt;p&gt;
Все личные имена, встречающиеся в статье, приведены с разрешения личностей, эти имена носящих. 
&lt;br/&gt;
 
&lt;br/&gt;
 
&lt;br/&gt;
 
На сайте &lt;a href=&quot;http://www.pic24.ru&quot; class=&quot;urlextern&quot; title=&quot;http://www.pic24.ru&quot;  rel=&quot;nofollow&quot;&gt;www.pic24.ru&lt;/a&gt; статья размещена с разрешения &lt;span class=&quot;important&quot;&gt;Alex'а B.&lt;/span&gt;
&lt;br/&gt;
 
&lt;br/&gt;
 
&lt;/p&gt;

&lt;p&gt;
Виктор Тимофеев, февраль 2009&lt;br/&gt;
 
&lt;a href=&quot;mailto:&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot; class=&quot;mail JSnocheck&quot; title=&quot;&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot;&gt;osa@pic24.ru&lt;/a&gt;
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Заключение&quot; [154580-] --&gt;</description>
    </item>
    <item rdf:about="http://www.pic24.ru/doku.php/osa/articles/rtos_usage_1?rev=1266955627">
        <dc:format>text/html</dc:format>
        <dc:date>2010-02-23T23:07:07+03:00</dc:date>
        <title>Программирование микроконтроллеров с использованием ОСРВ OSA</title>
        <link>http://www.pic24.ru/doku.php/osa/articles/rtos_usage_1?rev=1266955627</link>
        <description>


&lt;h1&gt;&lt;a name=&quot;программирование_микроконтроллеров_с_использованием_осрв_osa&quot; id=&quot;программирование_микроконтроллеров_с_использованием_осрв_osa&quot;&gt;Программирование микроконтроллеров с использованием ОСРВ OSA&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;

Виктор Тимофеев, &lt;a href=&quot;mailto:&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot; class=&quot;mail JSnocheck&quot; title=&quot;&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot;&gt;osa@pic24.ru&lt;/a&gt; (февраль, 2009)
&lt;/p&gt;

&lt;p&gt;
(ОСРВ - операционная система реального времени) 
&lt;/p&gt;

&lt;p&gt;
Два слова о том, что побудило написать эту статью. Хоть мне и не часто приходят письма с вопросами по ОСРВ OSA (отписываются не более 10 человек за месяц), тем не менее, этого достаточно, чтобы набрать статистику по часто совершаемым ошибкам. Я уже несколько раз отвечал на одни и те же вопросы и давал одни и те же советы, поэтому я решил все это дело обобщить в одной статье и выложить на всеобщее обозрение. Полагаю, что  статья окажется полезной не только тех, кто только начинают писать программы с использованием ОСРВ, но и для тех, кто уже имеют некоторый опыт. 
&lt;/p&gt;

&lt;p&gt;
Статья разделена на несколько частей: 
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#зачем_контроллеру_ось&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Зачем контроллеру &amp;quot;ось&amp;quot;?&lt;/a&gt; - здесь приводятся предпосылки и аргументы в пользу использования ОСРВ в некоторых проектах;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#общие_рекомендации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Общие рекомендации&lt;/a&gt; - некоторые рекомендации по использованию ОСРВ;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#faq_по_осрв_osa&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;FAQ по ОСРВ OSA&lt;/a&gt; - самые частые вопросы от программистов;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Советы по оптимизации&lt;/a&gt; - некоторые приемы и рекомендации по конфигурированию OSA для поднятия производительности системы;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#заключение&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Заключение&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Статья посвящена использованию ОСРВ в малоресурсных контроллерах. Однако, большинство рекомендаций (которые, конечно же, не бесспорны) могут быть приняты на вооружение применительно к остальным контроллерам.
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Программирование микроконтроллеров с использованием ОСРВ OSA&quot; [1-2779] --&gt;
&lt;h2&gt;&lt;a name=&quot;зачем_контроллеру_ось&quot; id=&quot;зачем_контроллеру_ось&quot;&gt;Зачем контроллеру &amp;quot;ось&amp;quot;?&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Прежде чем ответить на этот вопрос, попробуем определиться с тем, что мы называем операционной системой реального времени (ОСРВ) применительно к контроллерам. 
&lt;/p&gt;

&lt;p&gt;
Четкого определения самого понятия ОСРВ не существует, тем не менее большинство определений схожи в одном: ОСРВ - это операционная система, способная среагировать на событие в заданный промежуток времени. Но даже здесь есть допуск, а именно - разделение ОСРВ на системы мягкого реального времени и жесткого реального времени; первые отличаются от вторых как раз тем, что они допускают неопределенную задержку реакции на событие, в то время как для вторых эта задержка не должна превышать какого-то фиксированного значения (а если превысит, то это будет считаться системной ошибкой). 
&lt;/p&gt;

&lt;p&gt;
OSA относится к системам мягкого реального времени, т.к., являясь кооперативной, допускает захват ресурсов низкоприоритетной задачей на длительное время, не давая выполниться более приоритетным, т.е. не давая остальным задачам среагировать на событие в заданный промежуток времени. Этого недостатка лишены вытесняющие ОСРВ, в которых ядро в любой момент может прервать выполнение низкоприоритетной задачи и передать управление готовой к выполнению более приоритетной. Однако, вытесняющая ОСРВ не сможет работать на платформе без программно доступного стека. Контроллеры PIC серий 10, 12, 14 и 16 имеют именно такую платформу. Но зачем же тогда OSA поддерживает и старшие семейства этих контроллеров, если для них уже возможно использование вытесняющих ОСРВ? Здесь есть две причины: 
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; кооперативная ОСРВ требует на порядок меньше RAM-памяти под свои нужды, оставляя программисту большую часть памяти под нужды самой программы;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; кооперативная ОСРВ проще в понимании и в организации программ с ее использованием.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Зачем контроллеру ось?&quot; [2780-6053] --&gt;
&lt;h3&gt;&lt;a name=&quot;что_хорошего_дает_использование_осрв&quot; id=&quot;что_хорошего_дает_использование_осрв&quot;&gt;Что хорошего дает использование ОСРВ&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Так зачем же нужна операционная система в контроллерах? Я бы назвал несколько причин. 
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Во-первых, ОСРВ предоставляет программисту готовый планировщик&lt;/strong&gt; - некую подпрограмму, следящую за состоянием выполняемых задач, обеспечивающую поиск наиболее важной на данный момент и передачу ей управления. Поскольку часто на контроллер возлагается выполнение сразу нескольких задач (например, чтение клавиатуры, вывод данных на дисплей и пр.), то программист, создавая очередную программу, так или иначе вынужден каждый раз с нуля писать некий свой планировщик (в простейшем случае - это суперлуп, где все задачи-подпрограммы вызываются по очереди в бесконечном цикле). При этом в большинстве случаев не учитывается важность (приоритет) подпрограмм, вызываемых таким планировщиком, и подпрограмма, требующая больше внимания к себе со стороны планировщика, будет, тем не менее, получать его поровну со всеми остальными. Так вот, ОСРВ обеспечивает программиста уже готовым планировщиком, способным не только самостоятельно запускать все подпрограммы, но еще и определять, нужно ли какую-то конкретную подпрограмму запускать в данный момент, или нет, а также при обнаружении нескольких готовых к выполнению подпрограмм выбирать из них наиболее важную и передавать управление именно ей. 
&lt;em&gt;(Примечание: в ОСРВ такие подпрограммы называются задачами.)&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Во-вторых, ОСРВ обеспечивает параллельное выполнение всех задач&lt;/strong&gt;. В программе, написанной без применения ОСРВ, функция не может прерваться так, чтобы при повторном ее вызове продолжить свое выполнение с того места, где она была прервана. Разумеется, можно прибегнуть ко всякого рода хитростям, вроде таблицы переходов в начале функции или явного сохранения адреса возврата перед выходом, но зачем все это делать вручную, если ОСРВ уже имеет такую возможность? Кроме того, прибегая к таким сложным и нестандартным приемам программирования, как сохранение адреса, можно напортачить и налепить серьезных ошибок, незаметных на первый взгляд. Да и таблицы переходов в начале функции требуют постоянного &amp;quot;ухода&amp;quot; за ними: появляется новая точка выхода из функции - нужно добавить еще один переход в таблицу; убирается точка выхода - нужно убрать переход. Итак, ОСРВ предоставляет нам инструмент, позволяющий покинуть функцию-задачу в любом месте и оставляющий нам уверенность в том, что при следующем входе в задачу мы продолжим ее выполнение с того же места, откуда мы ее покинули.
&lt;/p&gt;

&lt;p&gt;
&lt;em&gt;(Примечание: здесь речь, конечно, идет о псевдопараллельности, т.е. когда все задачи выполянются по очереди, быстро сменяя друг друга; порядок очереди определяется приоритетами и готовностью задач к выполнению.)&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;В-третьих, ОСРВ берет на себя отсчет временных задержек&lt;/strong&gt;. Часто в программе нужно выдержать паузу (например при записи в EEPROM мы ждем 5-10 мс). Выполняя задержку явно, т.е., зацикливая программу до тех пор, пока какой-нибудь счетчик не достигнет нужного значения, мы блокируем работу всей программы на время выполнения этой задержки. Не страшно, если нам нужно подождать 10 мс после записи одного байта в EEPROM. А если нам нужно записать 100 байт? Придется &amp;quot;вешать&amp;quot; программу на секунду? ОСРВ же дает возможность организовать программу так, чтобы во время выполнения задержки одной задачей могли выполняться остальные задачи. Т.е. отсутствует время пустого простоя системы. Кроме того, часто в программах требуется ожидать некоего события, но так, чтобы была возможность прервать ожидание, если событие не происходит в течение какого-то времени. Простейший пример - набор номера в GSM-модеме и ожидание ответа. Мы не можем ждать вечно, поэтому дополняем программу неким механизмом, который по истечении заданного времени прервет ожидание. Для этого мы заводим отдельный таймер (тактируемый, например, в прерывании) и следим за его значением. В этом есть некоторые неудобства. Во-первых, на время ожидания  все остальные задачи блокированы; во-вторых, нужна отдельная глобальная переменная нужной размерности, имеющая осмысленное имя (например, ModemAnswerTimer); в-третьих, нужно не забыть все такие переменные инкрементировать в обработчике прерываний; есть еще в-четвертых и в-пятых, но это уже мелочи. ОСРВ же может обеспечить ожидание события с выходом по таймауту так, что оно будет лишено перечисленных недостатков, особенно - главного из них: система не будет простаивать впустую во время ожидания.
&lt;/p&gt;

&lt;p&gt;
&lt;p&gt;&lt;div class=&quot;noteclassic&quot;&gt;&lt;strong&gt;(Комментарий Alex B.)&lt;/strong&gt; Тут дело даже не в какой-то абстрактной блокировке остальных задач. Очень часто 5 мс это действительно незаметный отрезок. До тех пор, пока во вводных основной проблемой не станет энергопотребление. Несмотря на то, что устройство может быть не автономным, уменьшение интегрального потребления есть серьезный плюс к ТТХ вашего девайса.
&lt;/div&gt;&lt;/p&gt;
&lt;/p&gt;

&lt;p&gt;

&lt;strong&gt;В-четвертых&lt;/strong&gt;. В большинстве случаев подпрограммы, отвечающие за разные функциональные узлы, должны обмениваться между собой информацией. Например, подпрограмма, производящая опрос клавиатуры, должна сообщать остальным подпрограммам, какая кнопка была нажата/отпущена; а подпрограмма, выводящая информацию на экран, должна эту информацию откуда-то получать. Т.е. программа должна иметь какой-то механизм обмена информацией между внутренними функциями. Тут есть несколько вариантов: через аргументы функций, через глобальные переменные, наконец, через сервисы операционной системы (семафоры, сообщения, флаги). А как быть, когда одна задача должна получать данные от нескольких задач сразу, или когда какая-то задача отправляет данные быстрее, чем задача-получатель успевает их обрабатывать? Вот тут на помощь приходит очень &lt;strong&gt;полезный механизм ОСРВ - очереди сообщений&lt;/strong&gt;. Они позволяют задаче-отправителю отправлять следующее сообщение (какую-то полезную информацию), если предыдущее еще не успело обработаться задачей-получателем. Все это, конечно же, можно реализовать и без ОСРВ, но зачем выполнять уже выполненную работу, к тому же уже проверенную и отлаженную.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;В-пятых, использование ОСРВ улучшает читабельность и наглядность программного кода&lt;/strong&gt;. То, что реализуется без ОСРВ в 10-15 строках кода, с ОСРВ может быть записано одной строкой. Здесь я не буду приводить конкретных примеров, пока поверьте мне на слово, а когда дойдет дело до написания программы, сами убедитесь в том, что это так.
&lt;/p&gt;

&lt;p&gt;
Наконец, &lt;strong&gt;в-шестых&lt;/strong&gt;, последний аргумент в пользу применения ОСРВ - &lt;strong&gt;удобство написания программы, когда над ней трудятся несколько человек&lt;/strong&gt;. Применительно к PIC-контроллерам, конечно, это редкость, но тем не менее - это большой плюс.
&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Что хорошего дает использование ОСРВ&quot; [6054-17853] --&gt;
&lt;h3&gt;&lt;a name=&quot;что_мы_теряем_используя_осрв&quot; id=&quot;что_мы_теряем_используя_осрв&quot;&gt;Что мы теряем, используя ОСРВ&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

За все выше перечисленные полезности придется платить. 
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Платим мы в первую очередь памятью контроллера&lt;/strong&gt;. Сама ОСРВ требует под себя некоторые ресурсы, пусть небольшие, но все-таки. Чем больше параллельных задач, тем больше RAM потребуется для хранения их текущих состояний, адресов возврата и внутренних таймеров. Кроме того, система оперирует еще и своими внутренними переменными, такими как указатель на текущую задачу, лучший приоритет и пр. Ну, естественно, сами системные сервисы и планировщик требуют под себя программную память.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Во вторую очередь мы платим системным временем&lt;/strong&gt;. Поиск самой важной их готовых к выполнению задач не происходит мгновенно. На это требуется время. Чем больше задач, тем дольше будет выполняться поиск. Это следует учитывать при проектировании программы.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;В третью очередь, как ни печально, мы платим непереносимостью программного кода&lt;/strong&gt;. Т.е. переносимость программы с одной платформы на другую возможна только при том условии, что другая платформа так же поддерживается выбранной ОСРВ. Здесь конкретно для OSA выбор пока ограничен только PIC-контроллерами серий 10, 12, 14, 16, 18, 24, 30 и 33. Расширение планируется, но не сегодня и не завтра. Эта задача на перспективу.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Наконец, опасениями, что &amp;quot;код разработан не мной, а каким-то дядей&amp;quot;&lt;/strong&gt;, и кто знает, что у этого дяди было на уме. Я сам работал с чужими библиотеками и помню, что когда натыкался на какую-то ошибку, всегда в первую очередь грешил на библиотеки. Некоторые идут дальше: не в библиотеке - так в компиляторе, не в компиляторе - так в программаторе, не в программаторе - так в контроллере! И вот уже строчат письмо на support.microchip.com: &amp;quot;Я лажу нашел! В ваших контроллерах RA4 неправильно работает!&amp;quot; Однако, 99% всех ошибок - на совести программиста. Тем не менее, не исключены и ошибки библиотек, компиляторов, программаторов, наконец, самих контроллеров (не случайно ведь появляются документы ERRATA). И ОСРВ здесь - не исключение. Те, кто отписываются мне по OSA, бывает, обнаруживают неправильное поведение системы, помогают отлавливать баги. И чем больше народа отписывается, тем более безглючной становится &amp;quot;ось&amp;quot;. После отлова очередного бага (а они все хитрее и все трудноуловимее) всегда кажется, что вот теперь-то багов точно нет. Тем не менее, нет-нет, да обнаружится еще один (еще более хитрый). Поэтому вряд ли я когда-нибудь скажу: &amp;quot;Все, пользуйтесь. Багов нет!&amp;quot; (Да и ни один производитель ПО такого не скажет). Поэтому сидеть и ждать, когда система станет абсолютно безглючной, - это так никогда и не приступить к работе с ней. А попробовать стоит хотя бы из любопытства. Если и решите: &amp;quot;нет, это не для меня&amp;quot;, - то хотя бы, я уверен в этом, почерпнете для себя что-то новое.

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Что мы теряем, используя ОСРВ&quot; [17854-22736] --&gt;
&lt;h3&gt;&lt;a name=&quot;можно_ли_обойтись_без_осрв&quot; id=&quot;можно_ли_обойтись_без_осрв&quot;&gt;Можно ли обойтись без ОСРВ?&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Ответ однозначный: да, можно. При проектировании программы программист должен предварительно взвесить все выше перечисленные аргументы, свои аргументы, свои предпочтения, элементную базу и пр. и принять решение, что ему будет быстрее/удобнее/дешевле. 
&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Можно ли обойтись без ОСРВ?&quot; [22737-23267] --&gt;
&lt;h3&gt;&lt;a name=&quot;резюме&quot; id=&quot;резюме&quot;&gt;Резюме&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

&lt;p&gt;&lt;div class=&quot;noteimportant&quot;&gt;
ОСРВ содержит набор функций и инструментов, которые при написании программы без ОСРВ волей-неволей приходится каждый раз создавать вручную (планировщик, таймеры, сервисы обмена информацией). А создавать - это не только писать программы и подпрограммы, но еще и отлаживать их. ОСРВ же имеет весь этот набор уже отлаженный и готовый к применению. Тем самым программисту предоставляется возможность сконцентрироваться на решении конкретных проблемно-ориентированных задач, не отвлекаясь на системные задачи, которые берет на себя ОСРВ. Однако, необходимо помнить и о том, что сама операционная система требует под свои нужды ресурсы, и если в контроллере, на котором предполагается делать проект, их мало, то стоит предварительно хорошенько взвесить, потянет ли он эту программу, усиленную с ОСРВ.

&lt;/div&gt;&lt;/p&gt;
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Резюме&quot; [23268-] --&gt;</description>
    </item>
    <item rdf:about="http://www.pic24.ru/doku.php/osa/articles/rtos_usage_2?rev=1266955684">
        <dc:format>text/html</dc:format>
        <dc:date>2010-02-23T23:08:04+03:00</dc:date>
        <title>Общие рекомендации</title>
        <link>http://www.pic24.ru/doku.php/osa/articles/rtos_usage_2?rev=1266955684</link>
        <description>


&lt;h2&gt;&lt;a name=&quot;общие_рекомендации&quot; id=&quot;общие_рекомендации&quot;&gt;Общие рекомендации&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Некоторые присылают мне свои проекты, написанные с использованием OSA, с просьбой помочь разобраться, почему программа виснет или ведет себя не так, как хотелось бы, и пр. При разборе этих проектов я заметил, что многие при написании программы совершают две основные ошибки:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; пытаются использовать ОСРВ там, где это не нужно;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; любой фрагмент программы, который возможно написать с использованием сервисов ОСРВ, пишут, используя сервисы ОСРВ.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Эти два момента будут рассмотрены в первую очередь, а далее будут даны еще несколько рекомендаций по использованию ОСРВ.
&lt;/p&gt;

&lt;p&gt;

&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#не_используйте_осрв_там_где_это_не_нужно&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Не используйте ОСРВ там, где это не нужно&lt;/a&gt;&lt;br/&gt;
 
&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#не_злоупотребляйте_использованием_сервисов_осрв&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Не злоупотребляйте использованием сервисов ОСРВ&lt;/a&gt;&lt;br/&gt;
 
&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#не_забывайте_о_прерываниях&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Не забывайте о прерываниях&lt;/a&gt;&lt;br/&gt;
 
&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#о_глобальных_переменных&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;О глобальных переменных&lt;/a&gt;&lt;br/&gt;
 
&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#как_разбить_программу_на_задачи&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Как разбить программу на задачи&lt;/a&gt;&lt;br/&gt;
 
&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#как_расставлять_приоритеты&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Как расставлять приоритеты&lt;/a&gt;&lt;br/&gt;
 

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Общие рекомендации&quot; [1-2017] --&gt;
&lt;h3&gt;&lt;a name=&quot;не_используйте_осрв_там_где_это_не_нужно&quot; id=&quot;не_используйте_осрв_там_где_это_не_нужно&quot;&gt;Не используйте ОСРВ там, где это не нужно&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Далеко не все задачи удобно решать с помощью ОСРВ (а в некоторых случаях ОСРВ даже будет мешать). Кроме того, следует помнить, что микроконтроллеры (в особенности те, на которые в первую очередь была ориентирована OSA, т.е. PIC10, PIC12, PIC16) имеют дефицит ресурсов. Некоторые программисты после написания одной-двух программ с использованием ОСРВ так привязываются к ней, что, формулируя для себя очередную задачу еще в проблемно-ориентированных терминах, уже сразу пытаются подогнать эту формулировку под принципы ОСРВ. Т.е., фактически, при проектировании программы пропускается важный этап - выбор инструмента. Это не совсем правильно; вернее - совсем неправильно.
&lt;/p&gt;

&lt;p&gt;
Например, простая задача для PIC10: &amp;quot;пока на входе контроллера есть меандр 1 КГц, горит зеленый светодиод; если частота меандра выходит за пределы допуска +/- 10% - гасим зеленый светодиод и зажигаем красный&amp;quot;. Здесь использование ОСРВ совсем не оправдано, более того, просто вредно, поскольку работа планировщика отнимет скоростные ресурсы контроллера и не позволит обеспечить требуемую точность. Тем не менее, некоторые (не буду говорить, кто) создадут 3 независимых задачи: одна следит за меандром, вторая обрабатывает светодиоды, третья следит за первыми двумя  - и будут усердно пытаться всю остальную программу оформить и утрамбовать так, чтобы эти три задачи справлялись со своими функциями, причем - в ущерб наглядности текста, прозрачности алгоритма и, наконец, надежности самой программы.
&lt;/p&gt;

&lt;p&gt;
Другой пример - какую-нибудь простую задачу затолкают в малоресурсный контроллер вместе с ОСРВ, на этапе компиляции обнаружат, что памяти (или ROM, или RAM, или и той и другой) не хватает, и начинают вытворять какие-то немыслимые фокусы по оптимизации вплоть до использования машинных кодов в inline-ассемблере (есть такая возможность в HT-PICC), чтобы программу вообще хоть как-то запустить (Михаил, без обид :) ), при этом даже не рассмотрев вариант написания программы без использования ОСРВ.
&lt;/p&gt;

&lt;p&gt;
В ответ на мои рассуждения о ОСРВ и малоресурсных контроллерах мне приводят в пример мою же программу led3, мол, она-то с использованием ОСРВ написана; зачем тогда такой пример, если Вы сами не рекомендуете так делать? Здесь ответ простой. Представьте, что Вы пришли на базар, чтобы купить топор для колки дров. Продавец, демонстрируя свои топоры, одним из их разрубает стальной трос. На топоре при этом - ни зазубрины. Это не значит, что все стальные тросы нужно непременно рубить этими топорами, забыв при этом о специальных для таких целей инструментах, но это кое-что говорит о возможностях этих топоров в работе по дереву. Так же и с этим примером для PIC10. Он приведен не для того, чтобы показать, что любая программа для любого контроллера может и должна быть написана под операционной системой; он приведен для того, чтобы показать, насколько конкретная ОСРВ нетребовательна к ресурсам (в частности, ни jacOS ни Salvo с такой задачей не справятся). Другими словами, этот пример показывает, что само ядро операционной системы будет почти незаметно для программы при использовании контроллера помощнее, например, PIC16F628 (не говоря уже о PIC18 и выше).
&lt;/p&gt;

&lt;p&gt;
Резюме такое: разумно выбирайте инструмент для решения задачи. 
Программу, которая может получиться, следует оценивать не только по основным параметрам: 
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &amp;quot;выполняет возложенные на нее функции&amp;quot;;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &amp;quot;лишь бы влезла в выбранный контроллер&amp;quot;;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &amp;quot;использованы продвинутые решения (ОСРВ)&amp;quot;. &lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Важно учитывать также второстепенные: 
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; наглядность (читабельность) исходников;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; время написания программы;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; удобство (и написания, и отладки);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; прозрачность алгоритма;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; возможность расширения программы (добавления каких-то новых функций);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; отсутствие каких-либо условностей (типа, у меня таймер никогда не будет успевать переполняться, потому что тра-та-та и бла-бла-бла)&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;p&gt;&lt;div class=&quot;noteimportant&quot;&gt;
Поэтому, если на этапе проектирования есть сомнения в том, что использование ОСРВ не совсем подходит для решения данной задачи, - лучше работать без нее. Сэкономите и время и нервы.

&lt;/div&gt;&lt;/p&gt;
&lt;/p&gt;

&lt;p&gt;
Есть религиозные войны &amp;quot;Си &amp;amp; асм&amp;quot;. Изучение перепалок на эту тему позволяет сделать вывод, что самые ярые &amp;quot;вояки&amp;quot; - это те, которые с противоположной стороной вопроса не очень знакомы. Возможно, на подходе религиозные войны &amp;quot;RTOS &amp;amp; !RTOS&amp;quot;. Я предлагаю не участвовать на передовой, ударяя себя пяткой в грудь и крича, что RTOS - сила, а осваивать и ОСРВ, и суперлуп, и прерывания (есть и такой вариант), и выбирать оптимальный для решения конкретной задачи.
&lt;/p&gt;

&lt;p&gt;
(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#общие_рекомендации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку общих рекомендаций&lt;/a&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Не используйте ОСРВ там, где это не нужно&quot; [2018-10201] --&gt;
&lt;h3&gt;&lt;a name=&quot;не_злоупотребляйте_использованием_сервисов_осрв&quot; id=&quot;не_злоупотребляйте_использованием_сервисов_осрв&quot;&gt;Не злоупотребляйте использованием сервисов ОСРВ&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

В присланных мне проектах я также замечал маниакальное использование сервисов ОСРВ везде: где нужно и где не нужно. Складывается такое ощущение, что некоторые программисты рассматривают ОСРВ как язык программирования, просто еще более высокого уровня, чем Си. И как при программировании на Си избегают использования встроенного ассемблера, так и при программировании под ОСРВ избегают использования самого Си. В результате такого подхода исходный текст программы сильно пестрит изобилием сервисных вызовов, которое просто затрудняет чтение программы. Применительно к OSA, возможно, есть и моя вина в том, что некоторые так пишут программы. OSA предоставляет изобилие сервисов для, в общем-то, простых операций, не требующих каких-то сложных пассажей с передачей управления ядру (достаточно посмотреть на сервисы установки/сброса флагов). По существу же, большое количество сервисов - это просто формализация простых операций в терминах конкретной ОС. Применительно к флагам эта формализация звучит так: есть сервисы ожидания флагов, значит должны быть и сервисы установки/сброса флагов (хотя по сути - это простые побитовые операции and и or) и их проверки ( == и != ). Т.е. есть некий объект ОС (флаг), следовательно, ОС должна обеспечить программиста сервисами по всевозможным операциям с объектом. На деле же получается довольно трудночитаемый текст.
&lt;/p&gt;

&lt;p&gt;
Однако, это не единственная причина такого яростного пичкания программы системными сервисами. Тут какой-никакой здравый смысл присутствует. Это вроде как стандартизация, все-таки флаг - объект ОС, и неизвестно, как там нутро этой ОС устроено и как оно его обрабатывает (не все же программисты заглядывают в исходники ОС, даже если они открыты). Есть еще одна причина: мышление в контексте ОСРВ заставляет людей местами необдуманно использовать сервисы ОС там, где это совсем не к месту. 
&lt;/p&gt;

&lt;p&gt;
В качестве примера можно привести использование счетного семафора в качестве счетчика цикла. Он, конечно, может применяться в этих целях, принципами ОСРВ это не возбраняется, но здравый смысл должен говорить за неэффективность такого подхода. Сравните два приведенных ниже фрагмента кода:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    OS_Csem_SetValue&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;csem, &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        OS_Csem_Accept&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;csem&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;OS_Csem_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;scem&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;--&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Здесь очевидно злоупотребление сервисами ОС. Возможно, этот пример кажется утрированным, тем не менее, это фрагмент реальной программы.
&lt;/p&gt;

&lt;p&gt;
Еще могу привести такой пример. В одной из присланных мне программ была подпрограмма приема данных по последовательному интерфейсу (не аппаратному). Там была заведена переменная mask, которая имела только один установленный бит и циклически сдвигалась после приема каждого импульса. А принимаемый байт записывался в системную переменную типа OST_FLAG. В результате код имел несколько комичный вид:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;co1&quot;&gt;// CLC - ножка CLOCK&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;// DIO - ножка DATA&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;// (прим. - В.Т.)&lt;/span&gt;
&amp;nbsp;
    OS_Cond_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;CLC&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DIO&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;  OS_Flag_Set_1&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;data, mask&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;      OS_Flag_Set_0&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;data, mask&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    mask &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;mask&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; ...&lt;/pre&gt;
&lt;p&gt;
Налицо совершенно бессмысленное использование сервиса &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Flag_Set_x, которое в данном случае не только не наглядно, но еще и сбивает с толку. Куда нагляднее выглядел бы код:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    OS_Cond_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;CLC&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DIO&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;  data |=  mask;
    &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;      data &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; ~mask;
&amp;nbsp;
    mask &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;mask&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; ...&lt;/pre&gt;
&lt;p&gt;
Еще в одной присланной мне программе человек так увлекся бинарными семафорами, что на них построил всю логику работы программы. У него было под 40 этих семафоров, и в результате он в них запутался, т.к. его программа выглядела уже не как последовательность действий, а как какая-то МДНФ на ПЛМ, которая хоть и выполняет свою функцию, но без пристальнейшего вглядывания имеет совсем непонятную логику работы. И модификация такой программы становится весьма затруднительной (Семен, это я о тебе :) ).
&lt;/p&gt;

&lt;p&gt;
Я могу привести еще несколько подобных примеров, но полагаю, что читающий и так понял, к чему я призываю. Самыми полезными сервисами ОСРВ являются сервисы ожидания и переключения контекста. Все остальное - формализованное дополнение.
&lt;/p&gt;

&lt;p&gt;
Поэтому я подведу итог: 
&lt;p&gt;&lt;div class=&quot;noteimportant&quot;&gt;
Сервисы ОСРВ должны применяться по принципу &amp;quot;если без них никак&amp;quot;, а не по принципу &amp;quot;если можно как-то извернуться и сделать это на сервисах ОС, то надо сделать именно так&amp;quot;. Программа не должна выглядеть как номенклатурный список складского учета, где все товары имеют идентификационный код, начинающийся с &amp;quot;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_&amp;quot;, она должна быть наглядной и понятной.

&lt;/div&gt;&lt;/p&gt;
&lt;/p&gt;

&lt;p&gt;
(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#общие_рекомендации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку общих рекомендаций&lt;/a&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Не злоупотребляйте использованием сервисов ОСРВ&quot; [10202-18289] --&gt;
&lt;h3&gt;&lt;a name=&quot;не_забывайте_о_прерываниях&quot; id=&quot;не_забывайте_о_прерываниях&quot;&gt;Не забывайте о прерываниях&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
Также распространенной ошибкой при проектировании является отказ от использования кода в прерываниях. Многие программисты пытаются всю программу представить непременно в виде задач ОС, полагая при этом, что прерывание не оформить как отдельную задачу, и, следовательно, внутри него ничего делать нельзя. В большинстве программ, присланных мне, код прерыввания выглядел примерно одинаково:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; interrupt isr &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    OS_EnterInt&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;T0IF&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
       T0IF &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
       OS_Timer&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
    OS_LeaveInt&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Разница была только в используемом таймере. Все же остальные обработчики прерываний были вынесены в отдельные задачи и выглядели примерно так:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task_Iinterrupt_INTF &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        OS_Cond_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;INTF&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        INTF &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
        &lt;span class=&quot;coMULTI&quot;&gt;/* Далее следует ко добработки прерывания */&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Неэффективность такого подхода очевидна:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; во-первых, теряются преимущества использования системы прерываний, а именно - скорость реакции на событие, вывод контроллера из sleep-режима, маскирование; &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; во-вторых, появляются несколько лишних задач, каждая из которых требует ресурсы: память и время (об этом будет сказано чуть ниже в разделе &amp;quot;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#как_разбить_программу_на_задачи&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Как разбить программу на задачи&lt;/a&gt;&amp;quot;)&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;p&gt;&lt;div class=&quot;noteimportant&quot;&gt;
ОСРВ призвана упростить процесс проектирования и написания программы, а не заменить какие-то аппаратные модули микроконтроллера (в частности - контроллер прерваний). Поэтому не нужно стесняться использовать систему прерываний, укоряя себя за то, что &amp;quot;это же не по правилам РТОС!&amp;quot;. Все по правилам.  

&lt;/div&gt;&lt;/p&gt;
&lt;/p&gt;

&lt;p&gt;
В дополнение напомню, что ОСРВ OSA имеет целый набор сервисов для работы внутри прерываний, позволяя вести обмен информацией с остальной программой в терминах ОСРВ.
&lt;/p&gt;

&lt;p&gt;
(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#общие_рекомендации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку общих рекомендаций&lt;/a&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Не забывайте о прерываниях&quot; [18290-21468] --&gt;
&lt;h3&gt;&lt;a name=&quot;о_глобальных_переменных&quot; id=&quot;о_глобальных_переменных&quot;&gt;О глобальных переменных&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Вопрос использования глобальных переменных довольно спорный. Одни утверждают, что глобальные переменные - это зло, т.к. их наличие усложняет переносимость отдельных функций, а также не исключает возможные коллизии из-за модификаций глобальной переменной в разных модулях и т.д. Другие говорят, что использование глобальных переменных сокращает ресурсозатраты на обмен информацией. Точно сказать, кто прав в этом вопросе, я думаю, нельзя. И те и другие по-своему правы. Нужно рассматривать каждый конкретный случай в отдельности, при этом следует принимать во внимание не только задачу, под которую пишется программа, но еще и специфику используемого контроллера, и особенности используемого компилятора, и сколько программистов трудятся над задачей и т.д. и т.п. 
&lt;/p&gt;

&lt;p&gt;
В идеале программа не должна содержать глобальных переменных. В реальности же нам приходится искать компромисс между структурностью программы и наличием свободных ресурсов (памяти и скорости). Создавая программу с помощью ОСРВ, можно, конечно, все обмены данными между задачами делать с помощью объектов ОС: семафоров, сообщений, очередей и пр. (все эти объекты, вообще-то, сами по себе являются глобальными переменными, но они имеют четкую формализацию в терминах используемой ОС, а потому сильно упрощают перенос функций в другие программы и практически исключают коллизии с модификацией переменных разными модулями). Тем не менее, такой подход не всегда оправдан по нескольким причинам:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; одна переменная может использоваться сразу несколькими задачами (например, переменная, показывающая текущий режим работы устройства); при каждом изменении такой переменной пришлось бы сообщать об этом сразу нескольким задачам, что накладно по ресурсам;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; программа окажется сильно перегруженной системными вызовами, что затруднит ее читабельность;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; усложняется логика самой программы;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; наконец, сами сервисы требуют под себя какие-то ресурсы, и с увеличением сервисных вызовов у нас уменьшается память под саму программу.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Поэтому в большинстве случаев, особенно, когда дело касается малоресурсных контроллеров, имеет смысл некоторые переменные делать глобальными. Тут еще раз напомню, что сервисы ОС лучше использовать только там, где без них никак, а не там, где их можно втиснуть. Хоть это утверждение и спорно в общем случае, но применительно к контроллерам я считаю такой подход правильным.
&lt;/p&gt;

&lt;p&gt;
(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#общие_рекомендации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку общих рекомендаций&lt;/a&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;О глобальных переменных&quot; [21469-25995] --&gt;
&lt;h3&gt;&lt;a name=&quot;как_разбить_программу_на_задачи&quot; id=&quot;как_разбить_программу_на_задачи&quot;&gt;Как разбить программу на задачи&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;
Эта задача не совсем тривиальна. Даже в каждом конкретном случае может быть несколько решений. В присланных мне проектах четко прослеживались две основные ошибки, совершаемые программистами при разбиении программы на задачи. Вернее сказать, не ошибки, а, как бы выразиться, две крайности, в которые бросаются программисты.
&lt;/p&gt;

&lt;p&gt;
Первая - программист пытается разбить программу на задачи так, чтобы каждая задача выполняла одно свое действие и не мешала работать другим. Хоть в теории такое решение и выглядит красиво, но на практике получается совсем не так. Иногда дело доходит до того, что на каждое реле, управляющее каким-то силовым выходом, заводится отдельная задача. Выглядит она довольно красиво:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task_Relay1 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        OS_Bsem_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;BS_RELAY1&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        RELAY1 &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
        OS_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        RELAY1 &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;

Но что у нас получается на практике? Если программа управляет десятком таких силовых выходов, то у нас заводится десять таких задач. С виду - все красиво и аккуратно. Но не следует забывать, что каждая активная задача в ОСРВ требует несколько байт оперативной памяти, где хранится ее текущее состояние, адрес возврата, ее таймер (все эти параметры хранятся в так называемом дескрипторе задачи). Например, для PIC16 размер дескриптора задачи 5 байт (он может быть и меньше, но мы рассматриваем общий случай), и для десяти задач потребуется 50 байт ОЗУ, что для PIC16 - просто расточительство. Кроме того, еще одна проблема, возникающая при большом количестве задач, - увеличивается время работы планировщика, т.е. то время, за которое планировщик успеет перебрать все задачи, определить, какие из них готовы к выполнению, и из этих готовых найти самую важную (т.е. имеющую высший приоритет). 
&lt;/p&gt;

&lt;p&gt;
Так же к недостаткам такого подхода можно отнести и то, что усложняется управление этими задачами. Чем больше задач требуют каких-то данных для своей работы (в нашем примере это всего лишь бинарные семафоры, но ведь некоторые задачи могут требовать и сообщения, и очереди сообщений), тем остальным задачам сложнее будет справляться с управлением такими потоками данных. 
&lt;/p&gt;

&lt;p&gt;
Можно привести еще пример (тоже из реальной жизни): на экран нужно было выводить информацию разного характера. Для этого были созданы 4 задачи, каждая из которых выводила на экран свой параметр. Каждая задача ожидала от головной задачи свое сообщение. В результате, помимо того, что для 4-х задач функции вывода на экран являлись разделяемым ресурсом, так еще и &lt;em&gt;головная задача была озадачена головной болью&lt;/em&gt; по работе с четырьмя сообщениями. Кроме того, такой подход накладывает ограничение на содержание выводимых на экран данных, ибо для вывода 5-го параметра потребовалась бы 5-я задача (и 5-е сообщение), для 6-го - 6-я и т.д. 
&lt;/p&gt;

&lt;p&gt;
Вторая крайность - это когда программист пытается все, что не является обработкой кнопок, затолкать в одну задачу. Она занимается и приемом даных по USART, и записью данных в EEPROM, и … да чем только не занимается! Зато информацию о кнопках получает извне. Зато есть многозадачность! Бессмысленность такого подхода очевидна (тем не менее, он часто встречается): нет преимущества использования ОСРВ. В чем преимущество параллельности выполнения задач, если задача всего одна? В чем преимущество очередей сообщений, если задача сообщения отсылает сама себе? Какова будет реакция на событие, если задача занимается сразу всем? Про читабельность кода, написанного с таким подходом, я вообще молчу. Единственное, что полезного можно ухватить от ОСРВ в таком случае, - это удобство формирования задержек (&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay - и все!).
&lt;/p&gt;

&lt;p&gt;
Итак, как же разбивать программу на задачи? Как я уже писал выше, единой рекомендации по этому вопросу нет. Тут я могу дать только несколько советов, исходя из опыта и здравого смысла. 
&lt;/p&gt;

&lt;p&gt;
Для начала приведу очевидный пример: сначала программа была написана под конкретный LCD-индикатор. По прошествии какого-то времени после запуска ее в серию в продаже появился новый менее дорогой и более функциональный индикатор, и руководство фирмы приняло решение следующую версию устройства делать на нем. Очевидно, что если при написании программы все, относящееся к функциям вывода на экран, было вынесено в отдельный модуль (отдельную задачу), то и модификация программы будет минимальной. Т.е. меняем только функции работы с LCD и задачу вывода информации на экран (единственную задачу, которая работает с функциями LCD). Для остальной программы такая замена будет незаметной: она как отсылала свои сообщения в очередь, так и отсылает, а что там вытворяет с ними задача вывода на экран, - это уже их не касается.
&lt;/p&gt;

&lt;p&gt;

&lt;p&gt;&lt;div class=&quot;noteimportant&quot;&gt;
Другими словами: если какой-то внешний модуль допускает замену, то управление этим модулем нужно выносить в отдельную задачу. К таким модулям, помимо индикаторов, можно также отнести внешнюю память, протоколы обмена данными, клавиатуру, GPS- и GSM-модули и т.д.

&lt;/div&gt;&lt;/p&gt;
&lt;/p&gt;

&lt;p&gt;
Рассмотрим теперь такой пример. Допустим, программист работает в фирме, которая разрабатывает, скажем, системы доступа: домофоны, кодовые замки, пульты дистанционного управления и пр.
&lt;/p&gt;

&lt;p&gt;
Почти в каждом из разрабатываемых фирмой устройств есть кнопки. И чтобы не писать функции обработки кнопок для каждого такого устройства, есть смысл вынести обработку кнопок в отельную задачу, оформить ее в виде отдельного файла и использовать во всех проектах (изменяя только количество кнопок, полярность и пр.).
&lt;/p&gt;

&lt;p&gt;

&lt;p&gt;&lt;div class=&quot;noteimportant&quot;&gt;
Итак, если предполагается в будущем какой-то кусок текущей программы использовать еще где-то, то такой кусок было бы удобно оформить в виде отдельной задачи.

&lt;/div&gt;&lt;/p&gt;
&lt;/p&gt;

&lt;p&gt;
Не нужно объединять в одной задаче функционально различные модули. Например объединять вывод звука с чтением данных по UART. Пускай даже и удастся написать задачу так, что эти функции не будут мешать и задерживать друг друга, но концептуально это неправильно, т.к. программа теряет наглядность. Т.е. лучше делать задачу максимально наглядной.
&lt;/p&gt;

&lt;p&gt;
При вынесении каких-то функций в отдельную задачу нужно предварительно прикинуть, сможет ли задача при такой организации обеспечить реакцию на событие в течение определенного времени. Это время может быть различным в зависимости от события. Например, если пользователь нажимает на кнопку, то он хочет увидеть реакцию устройства сразу же, и задержка всего в секунду уже недопустима. Если событие - это превышение температуры в комнате на 2 градуса выше какой-то нормы, то тут допустима задержка реакции в 5 секунд или в минуту - разницы особой нет. Т.е. задача должна быть спроектирована таким образом, чтобы ее выполнение не мешало ей же реагировать на события. Например, у нас есть задача, управляющая несколькими силовыми приборами: свет, насосы, обогреватель. Получая сообщение о том, что нужно на 10 секунд включить насос, она в течение этих 10 секунд может оказаться не в состоянии обработать другие приходящие ей сообщения. Если логика работы такой задачей проста, то эту проблему можно обойти, добавив в программу несколько таймеров и булевых переменных. Однако же, если логика посложнее, то тогда, наверное, есть смысл разбить эту задачу на две или более.
&lt;/p&gt;

&lt;p&gt;
В одном из присланных мне проектов был реализован интересный подход, который тоже можно взять на вооружение. В программе было описано около 20 функций-задач. Но в один момент времени могли работать только 4. 3 из них работали постоянно, а 4-я в зависимости от текущего режима все время пересоздавалась. Преимущество такого подхода - скорость работы планировщика (чем меньше активных задач, тем планировщик OSA работает быстрее). Недостаток - нужно очень внимательно следить за создаваемыми/удаляемыми задачами, а так же за тем, чтобы при удалении они не забывали освобождать занимаемые ресурсы.
&lt;/p&gt;

&lt;p&gt;

Попробую подытожить рекомендации. Написанная программа должна обладать следующими свойствами:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; она должна правильно выполнять возложенные на нее функции. Это свойство к разбиению программы на задачи отношения, в общем-то, не имеет, но если программа не обладает этим свойством, то нет смысла рассматривать все остальные. Поэтому для полноты картины это свойство оставим на первом месте.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; она должна успевать обрабатывать события в заданный промежуток времени. Следовательно, при проектировании задач должно быть учтено время работы самой задачи.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; она должна быть дружественной программисту. Не только в том смысле, что программист при написании соблюдал все правила оформления исходных текстов (отступы, пробелы, пустые строки, выровненные комментарии и т.п.), но и в том, чтобы, глядя в программу, можно было бы прочитать алгоритм работы устройства.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; она должна быть модульной. Т.е., во-первых, иметь возможность быстрой замены одного модуля другим (как в приведенном выше примере про LCD-индикаторы), и во-вторых, оставлять возможность использовать уже написанные куски кода в других приложениях.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; она не должна содержать слишком много простых задач или слишком мало сложных.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Напомню, что все выше перечисленное носит исключительно рекомендательный характер. Возможно, кто-то руководствуется другими правилами. Тем не менее, часто приходится видеть программы людей, которые каким-либо правилам следуют условно (т.е. не следуют им совсем), поэтому я надеюсь, что эти рекомендации кому-то окажутся полезными.
&lt;/p&gt;

&lt;p&gt;
(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#общие_рекомендации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку общих рекомендаций&lt;/a&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Как разбить программу на задачи&quot; [25996-42663] --&gt;
&lt;h3&gt;&lt;a name=&quot;как_расставлять_приоритеты&quot; id=&quot;как_расставлять_приоритеты&quot;&gt;Как расставлять приоритеты&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Применительно к кооперативной ОСРВ, приоритет задачи - понятие весьма условное. Оно больше подходит к вытесняющим ОСРВ, где планировщик может приостановить задачу в любой момент, чтобы передать управление готовой более приоритетной задаче, что обеспечивает выполнение важного условия - детерминированного времени реакции на событие. В кооперативной ОСРВ вытеснение произойти не может, т.е. планировщик не может отнять у задачи управление, а только сама задача может решить, отдавать управление планировщику или нет. Поэтому приоритетность в кооперативных ОСРВ не совсем уместна. 
&lt;/p&gt;

&lt;p&gt;
Кроме того, включение механизма приоритетов несколько замедляет работу планировщика, поскольку, помимо перебора всех задач в поиске готовой, он будет должен еще заниматься сравниванием их приоритетов и поиском максимального. Поэтому, как это ни парадоксально, работая в приоритетном режиме, задача с высоким приоритетом может получить управление с большей задержкой, чем если бы приоритеты были отключены.
&lt;/p&gt;

&lt;p&gt;
Тем не менее, в программах под кооперативными ОСРВ бывают случаи, когда приоритетность выручает.
&lt;/p&gt;

&lt;p&gt;
Например, при сильной загрузке контроллера. Если контроллер сильно загружен какими-то вычислениями, или общением с внешними устройствами, чтением датчиков и т.д., то возникают ситуации, когда он не в состоянии, выполняя все задачи по очереди, обеспечить реакцию на некоторые события в приемлемое время. Включение приоритетного режима позволит быстрым высокоприоритетным задачам выполняться чаще остальных, позволяя сократить время реакции.
&lt;/p&gt;

&lt;p&gt;
Другой пример: при происшествии какого-то события нам нужно выполнить несколько задач в определенной последовательности. Все задачи находятся в ожидании установки какого-то бита (пусть это будет флаг прерывания INTF). Тогда расстановка приоритетов задачам позволит нам однозначно определить порядок выполнения задач при установке INTF. Последняя задача, отработавшая по этому событию (т.е. самая низкоприоритетная), этот флаг сбросит.
&lt;/p&gt;

&lt;p&gt;
В большинстве же случаев в программах под кооперативной ОСРВ приоритеты задачам не нужны. Однако, если все же решили использовать приоритетный режим, то как тогда правильно расставить приоритеты? 
&lt;/p&gt;

&lt;p&gt;
Для того, чтобы ответить на этот вопрос, нужно посмотреть на задачи не как программист, например, &amp;quot;хочу, чтобы опрос кнопок производился с интервалом 20 мс&amp;quot;, а как пользователь, т.е. &amp;quot;хочу, чтобы при нажатии на кнопку, сразу происходило действие&amp;quot;. При такой постановке вопроса становится ясно, что не столь важно опрашивать кнопку именно каждые 20 мс, сколь успеть ее обработать сразу при нажатии. При этом уже очевидно, что интервал может быть и 20 мс и 40 мс, т.е. не так критичен, а отсюда становится понятно, что приоритет этой задачи нет смысла делать высоким. 
&lt;/p&gt;

&lt;p&gt;
Высокоприоритетными задачами нужно делать те, которые действительно критичные ко времени реакции. Представьте себе устройство управления автоматическими воротами. Задачами этого устройство являются: прием по радиоканалу команд от ПДУ на открытие/закрытие, управление двигателями дверей, а также - аварийный реверс двигателей, если на пути створок дверей оказалось препятствие. Последняя задача является критичной ко времени. Например, оператор нажал копку на ПДУ - ворота открылись, машина поехала, а оператор нажал кнопку закрытия ворот раньше времени. В результате створки ворот упрутся в кузов автомобиля. Чем быстрее устройство сообразит, что надо дать дверям обратный ход, тем дешевле будут последствия.
&lt;/p&gt;

&lt;p&gt;
Не следует давать высокий приоритет задачам, которые долго выполняются. Например, в программе одновременно оказались готовыми к выполнению задача включения реле и задача преобразования Фурье. Очевидно, что вторая заберет ресурсы контроллера на долгое время, поэтому есть смысл пропустить вперед быструю задачу включения реле, после чего уже можно отдавать управление вычислителю.
&lt;/p&gt;

&lt;p&gt;
Ко всему сказанному можно добавить, что большинство ОСРВ (OSA - не исключение) позволяют менять приоритет задач в ходе выполнения программы, что может в некоторых случаях оказаться полезным. Например, нет смысла давать высокий приоритет задаче записи на flash-карту в фотоаппарате, если сам фотоаппарат находится в режиме просмотра снимков, и запись в данный момент ему нужна только для сохранения каких-то единичных параметров, вроде текущей яркости экрана, или номера последнего просмотренного снимка. И наоборот, задачу записи во flash-память лучше сделать самой приоритетной во время сохранения снимка, пожертвовав при этом скоростью вывода на экран, скоростью обработки кнопок и т.д.
&lt;/p&gt;

&lt;p&gt;
Вот, собственно, основные рекомендации по расстановке приоритетов. В целом эта задача так же не тривиальна, как и разбиение программы на задачи, и в каждом случае нужно решать ее отдельно. Но рекомендации, приведенные выше, помогут упростить этот процесс. 
&lt;/p&gt;

&lt;p&gt;
(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#общие_рекомендации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку общих рекомендаций&lt;/a&gt;)
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Как расставлять приоритеты&quot; [42664-] --&gt;</description>
    </item>
    <item rdf:about="http://www.pic24.ru/doku.php/osa/articles/rtos_usage_3?rev=1266955756">
        <dc:format>text/html</dc:format>
        <dc:date>2010-02-23T23:09:16+03:00</dc:date>
        <title>FAQ по ОСРВ OSA</title>
        <link>http://www.pic24.ru/doku.php/osa/articles/rtos_usage_3?rev=1266955756</link>
        <description>


&lt;h2&gt;&lt;a name=&quot;faq_по_осрв_osa&quot; id=&quot;faq_по_осрв_osa&quot;&gt;FAQ по ОСРВ OSA&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Здесь я собрал ответы на самые распространенные вопросы по OSA.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#можно_ли_вызывать_сервисы_ожидания_из_функций_вызываемых_задачами&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Можно ли вызывать сервисы ожидания из функций, вызываемых задачами?&lt;/a&gt;&lt;br/&gt;
 
&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#ожидание_события_в_цикле&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Ожидание события в цикле&lt;/a&gt;&lt;br/&gt;
 
&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#можно_ли_создать_задачу_по_указателю_на_функцию&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Можно ли создать задачу по указателю на функцию?&lt;/a&gt;&lt;br/&gt;
 
&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#что_будет_если_отсылатьпринимать_сообщения_из_неинициализированной_очереди&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Что будет, если отсылать/принимать сообщения из неинициализированной очереди?&lt;/a&gt;&lt;br/&gt;
 
&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#изменение_типов_сообщений&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Изменение типов сообщений&lt;/a&gt;&lt;br/&gt;
 
&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#если_две_задачи_ожидают_одного_и_того_же_события&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Если две задачи ожидают одного и того же события&lt;/a&gt;&lt;br/&gt;
 
&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#модификация_тела_сообщения_до_того_как_оно_будет_принято&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Модификация тела сообщения до того, как оно будет принято&lt;/a&gt;&lt;br/&gt;
 
&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#использование_таймеров_вне_задач&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Использование таймеров вне задач&lt;/a&gt;&lt;br/&gt;
 
&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#почему_виснет_os_delay&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Почему виснет OS_Delay?&lt;/a&gt;

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;FAQ по ОСРВ OSA&quot; [1-1927] --&gt;
&lt;h3&gt;&lt;a name=&quot;можно_ли_вызывать_сервисы_ожидания_из_функций_вызываемых_задачами&quot; id=&quot;можно_ли_вызывать_сервисы_ожидания_из_функций_вызываемых_задачами&quot;&gt;Можно ли вызывать сервисы ожидания из функций, вызываемых задачами?&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

&lt;strong&gt;Ответ: нет.&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
Этот вопрос, пожалуй, задают чаще остальных. Такой подход, действительно, выглядит очень заманчивым, когда, например, в задачах часто вызывается сервис &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay  с одним и тем же параметром:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;co1&quot;&gt;// В этом примере просто напрашивается вынос OS_Delay(10) в отдельную функцию.&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;// Сам вызов сервиса занимает около 10 слов ROM, и его, конечно, хочется&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;// заменить одним вызовом.&lt;/span&gt;
    ...
    &lt;span class=&quot;me1&quot;&gt;OS_Delay&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    ...
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;...&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; OS_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    ...
    &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        ...
        &lt;span class=&quot;me1&quot;&gt;OS_Delay&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        ...
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;...&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    ...&lt;/pre&gt;
&lt;p&gt;
или когда есть несколько задач, ожидающих одно и то же событие:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    ...
    &lt;span class=&quot;co1&quot;&gt;// Ждем, когда освободится доступ в EEPROM&lt;/span&gt;
    OS_Bsem_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;BS_EEPROM_FREE&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    ...&lt;/pre&gt;
&lt;p&gt;
К сожалению в ОСРВ OSA такое недопустимо. Дело в том, что при таком подходе произойдет путаница с адресами возврата в стеке. Рассмотрим на примере:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co1&quot;&gt;//-----------------------------------------------&lt;/span&gt;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Delay10 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    OS_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//-----------------------------------------------&lt;/span&gt;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Bsem_Wait &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    OS_Bsem_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;BS_BINSEM&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//-----------------------------------------------&lt;/span&gt;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; TaskA &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        Delay10&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        Delay10&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        Delay10&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//-----------------------------------------------&lt;/span&gt;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; TaskB &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        Bsem_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        Bsem_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        Bsem_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//-----------------------------------------------&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Итак, у нас есть две задачи: TaskA и TaskB. Обе вызывают разные функции, в каждой из которых есть сервис, содержащий код возврата в планировщик (в перечне сервисов все такие сервисы в примечании отмечены буквой &amp;quot;T&amp;quot;). Для простоты рассмотрим работу этого примера на PIC16. &lt;em&gt;(Примечание: планировщик в этих контроллерах передает управление задачам присвоением адреса задачи паре регистров PCLATH:PCL, а задачи возвращают управление планировщику, совершая переход на него по GOTO. Таким образом экономится стек, т.к. ни задачи, ни планировщик не вызываются через CALL)&lt;/em&gt;. При выполнении программы возможна такая последовательность:
&lt;/p&gt;
&lt;table class=&quot;inline&quot;&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;th class=&quot;col0&quot;&gt; . &lt;/th&gt;&lt;th class=&quot;col1 centeralign&quot;&gt;  Действие  &lt;/th&gt;&lt;th class=&quot;col2 centeralign&quot;&gt;  Передача управления  &lt;/th&gt;&lt;th class=&quot;col3 centeralign&quot;&gt;  Стек  &lt;/th&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row1&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; 1 &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; Планировщик запускает задачу &lt;strong&gt;TaskA&lt;/strong&gt;  &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; PCLATH:PCL=TaskA &lt;/td&gt;&lt;td class=&quot;col3&quot;&gt; - &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row2&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; 2 &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Задача &lt;strong&gt;TaskA&lt;/strong&gt; вызывает функцию &lt;strong&gt;Delay10()&lt;/strong&gt;&lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; CALL Delay10. &lt;/td&gt;&lt;td class=&quot;col3&quot;&gt; &lt;strong&gt;ret_addr_A&lt;/strong&gt;&lt;br/&gt;
 - &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row3&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; 3 &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Функция &lt;strong&gt;Delay10&lt;/strong&gt; вызывает системный сервис &lt;strong&gt;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay&lt;/strong&gt;, который, инициализировав задержку, передает управление планировщику &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; GOTO sched &lt;/td&gt;&lt;td class=&quot;col3&quot;&gt; &lt;strong&gt;ret_addr_A&lt;/strong&gt;&lt;br/&gt;
 - &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row4&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; 4 &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Планировщик запускает задачу &lt;strong&gt;TaskB&lt;/strong&gt; &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; PCLATH:PCL=TaskB &lt;/td&gt;&lt;td class=&quot;col3&quot;&gt; &lt;strong&gt;ret_addr_A&lt;/strong&gt;&lt;br/&gt;
 - &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row5&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; 5 &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Задача &lt;strong&gt;TaskB&lt;/strong&gt; вызывает функцию &lt;strong&gt;Bsem_Wait()&lt;/strong&gt; &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; CALL Bsem_Wait &lt;/td&gt;&lt;td class=&quot;col3&quot;&gt; &lt;strong&gt;ret_addr_B&lt;/strong&gt;&lt;br/&gt;
 &lt;strong&gt;ret_addr_A&lt;/strong&gt;&lt;br/&gt;
 - &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row6&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; 6 &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Функция &lt;strong&gt;Bsem_Wait&lt;/strong&gt; вызывает системный сервис &lt;strong&gt;&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Bsem_Wait&lt;/strong&gt;, который передает управление планировщику &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; GOTO sched &lt;/td&gt;&lt;td class=&quot;col3&quot;&gt; &lt;strong&gt;ret_addr_B&lt;/strong&gt;&lt;br/&gt;
 &lt;strong&gt;ret_addr_A&lt;/strong&gt;&lt;br/&gt;
 - &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row7&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; 7 &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Планировщик крутится вхолостую, пока идет задержка, запущенная в задаче &lt;strong&gt;TaskA&lt;/strong&gt; и пока не установлен семафор, которого ожидает задача &lt;strong&gt;TaskB&lt;/strong&gt; &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; &lt;/td&gt;&lt;td class=&quot;col3&quot;&gt;&lt;strong&gt;ret_addr_B&lt;/strong&gt;&lt;br/&gt;
 &lt;strong&gt;ret_addr_A&lt;/strong&gt;&lt;br/&gt;
 - &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row8&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; 8 &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Задержка закончилась, плнировщик передает управление задаче &lt;strong&gt;TaskA&lt;/strong&gt; в то же место, откуда был возврат в планировщик, а именно - в середину функиции &lt;strong&gt;Delay10&lt;/strong&gt; &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; PCLATH:PCL=Delay10 &lt;/td&gt;&lt;td class=&quot;col3&quot;&gt;&lt;strong&gt;ret_addr_B&lt;/strong&gt;&lt;br/&gt;
 &lt;strong&gt;ret_addr_A&lt;/strong&gt;&lt;br/&gt;
 - &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row9&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; 9 &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; И теперь - кульминация: функция делает возврат, при котором из стека берется последний положенный туда адрес, а именно - &lt;strong&gt;ret_addr_B&lt;/strong&gt; &lt;/td&gt;&lt;td class=&quot;col2&quot;&gt; RETURN &lt;/td&gt;&lt;td class=&quot;col3&quot;&gt; &lt;strong&gt;ret_addr_A&lt;/strong&gt;&lt;br/&gt;
 - &lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;

Как видно, после 9-ой операции мы из функции &lt;strong&gt;Delay10&lt;/strong&gt; вернемся в функцию-задачу &lt;strong&gt;TaskB&lt;/strong&gt;, хотя должны были вернуться в &lt;strong&gt;TaskA&lt;/strong&gt;.
&lt;/p&gt;

&lt;p&gt;

&lt;em&gt;&lt;strong&gt;Примечание.&lt;/strong&gt; В принципе, такой подход допускается, если у программиста есть уверенность в том, что в один момент времени только одна задача производит вызов такой функции. Однако, здесь надо быть крайне осторожным и хорошо понимать, что он делает. Поэтому, если Вы не уверены, что уследите за вызовами при дальнейшем росте программы, - не применяйте такой прием!&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#faq_по_осрв_osa&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Вернуться к списку FAQ&lt;/a&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Можно ли вызывать сервисы ожидания из функций, вызываемых задачами?&quot; [1928-8157] --&gt;
&lt;h3&gt;&lt;a name=&quot;ожидание_события_в_цикле&quot; id=&quot;ожидание_события_в_цикле&quot;&gt;Ожидание события в цикле&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Некоторые из присланных мне программ содержали однотипную ошибку, которую я бы хотел здесь обрисовать. Иногда бывает так, что в ходе ожидания какого-либо события требуется выполнять какое-то действие. Поэтому код этого ожидания некоторые писали без использования сервисов &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_xxx_Wait, заменяя их циклом do {…} while. Рассмотрим отвлеченный пример: пока ожидаем установки какого-то двоичного семафора, нам нужно сравнивать напряжения на двух входах АЦП и, в зависимости от результата сравнения, зажигать либо красный либо зеленый светодиод. 
&lt;/p&gt;

&lt;p&gt;
Код такого ожидания выглядел так:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;ADC_Read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;&amp;gt;&lt;/span&gt; ADC_Read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;    &lt;span class=&quot;co1&quot;&gt;// Сравниваем напряжения на двух аналоговых входах&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
            GREEN_LED &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
            RED_LED &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
            GREEN_LED &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
            RED_LED &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
        OS_Yield&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                       &lt;span class=&quot;co1&quot;&gt;// Возврат в планировщик&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_Bsem_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;BS_START&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Ошибка такого подхода заключается в том, что задача, крутясь в таком цикле, является &lt;strong&gt;всегда готовой&lt;/strong&gt; к выполнению. И если в программе есть задачи с более низким приоритетом, то они не смогут получить управление до тех пор, пока эта задача не дождется семафора. А если ожидаемый ей семафор должна установить как раз задача с более низким приоритетом, то программа просто зависнет в вечном ожидании. 
&lt;/p&gt;

&lt;p&gt;
Как быть в таких случаях? Здесь есть несколько вариантов решения этой коллизии.
&lt;/p&gt;

&lt;/div&gt;

&lt;h5&gt;&lt;a name=&quot;вынести_выполняемый_при_ожидании_код_в_отдельную_низкоприоритетную_задачу&quot; id=&quot;вынести_выполняемый_при_ожидании_код_в_отдельную_низкоприоритетную_задачу&quot;&gt;1. Вынести выполняемый при ожидании код в отдельную низкоприоритетную задачу&lt;/a&gt;&lt;/h5&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

С точки зрения концепции ОСРВ такой способ самый правильный. В данном конкретном примере сравнение напряжений на входах АЦП и ожидание семафора - функционально разные действия и нет никакого смысла выполнять их одновременно.
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;OST_TASK_POINTER tp;
&amp;nbsp;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Отдельная задача для работы с АЦП и светодиодами&lt;/span&gt;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task_ADC_Leds &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
   tp &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; OS_GetCurTask&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
   &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
   &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/* Здесь сравниваем напряжения */&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
&amp;nbsp;
        OS_Yield&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                       &lt;span class=&quot;co1&quot;&gt;// Возврат в планировщик&lt;/span&gt;
   &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Наша задача&lt;/span&gt;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;// Перед ожиданием создаем задачу сравнения напряжений&lt;/span&gt;
        OS_Task_Create&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;7&lt;/span&gt;, Task_ADC_Leds&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;// Ждем наш семафор&lt;/span&gt;
        OS_Bsem_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;BS_START&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;// Удаляем задачу сравнения напряжений&lt;/span&gt;
        OS_Task_Delete&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;tp&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Но при своей правильности этот подход не всегда оправдан, т.к. требует наличие свободного дескриптора на момент создания новой задачи, дополнительной глобальной переменной и времени на создание/удаление задачи.
&lt;/p&gt;

&lt;/div&gt;

&lt;h5&gt;&lt;a name=&quot;понизить_приоритет_на_время_ожидания&quot; id=&quot;понизить_приоритет_на_время_ожидания&quot;&gt;2. Понизить приоритет на время ожидания&lt;/a&gt;&lt;/h5&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Понизив приоритет до минимального, мы исключаем, что какая-либо задача окажется блокированной. С точки зрения ресурсов контроллера я бы назвал такой подход оптимальным.
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; prio;
    &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    prio &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; OS_Task_GetPriority&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;this_task&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;   &lt;span class=&quot;co1&quot;&gt;// Запоминаем текущий приоритет задачи&lt;/span&gt;
    OS_Task_SetPriority&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;this_task, &lt;span class=&quot;nu0&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;       &lt;span class=&quot;co1&quot;&gt;// Понижаем приоритет до минимального&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/* Здесь сравниваем напряжения */&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        OS_Yield&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_Bsem_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;BS_START&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    OS_Task_SetPriority&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;this_task, prio&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;    &lt;span class=&quot;co1&quot;&gt;// После цикла восстанавливаем сохраненный&lt;/span&gt;
                                             &lt;span class=&quot;co1&quot;&gt;// приоритет&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;em&gt;Примечание: рекомендуется понижать приоритет не до самого низкого (7-го), а до предпоследнего (6-го), т.к.низший приоритет удобно использовать для задачи SLEEP&amp;#039;а.&lt;/em&gt;
&lt;/p&gt;

&lt;/div&gt;

&lt;h5&gt;&lt;a name=&quot;вставить_в_цикл_небольшую_задержку&quot; id=&quot;вставить_в_цикл_небольшую_задержку&quot;&gt;3. Вставить в цикл небольшую задержку&lt;/a&gt;&lt;/h5&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Заменив &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Yield() на &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay(1), мы гарантировано на время одного системного тика ставим задачу в режим ожидания (время задержки можно увеличить, если одного тика мало):
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/* Здесь сравниваем напряжения */&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        OS_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_Bsem_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;BS_START&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Недостатком такого способа будет увеличение периода сравнения напряжений. Возможно, в данном примере это не страшно, но в другом случае, если операции внутри цикла критичны ко времени, это может отрицательно сказаться на логике работы устройства.
&lt;/p&gt;

&lt;p&gt;

(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#faq_по_осрв_osa&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Вернуться к списку FAQ&lt;/a&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Ожидание события в цикле&quot; [8158-15137] --&gt;
&lt;h3&gt;&lt;a name=&quot;можно_ли_создать_задачу_по_указателю_на_функцию&quot; id=&quot;можно_ли_создать_задачу_по_указателю_на_функцию&quot;&gt;Можно ли создать задачу по указателю на функцию?&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

&lt;strong&gt;Ответ: да&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
В OSA есть два недокументированных сервиса, которые позволяют это сделать. Я их не стал описывать в общей документации, чтобы не вносить путаницы в логику работы задач. Тем не менее, такой способ создания задач может оказаться очень удобным в пользовательских приложениях, где адрес функции-задачи привязан, например, к пункту меню. Предположим, у нас определен тип структуры, содержащей название пункта меню и адрес задачи, которую нужно будет выполнять по этому пункту:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;struct&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt; strMenu;
    &lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;  &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;Func&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; TMenuItem;&lt;/pre&gt;
&lt;p&gt;
Далее в программе определен массив этих структур:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;const&lt;/span&gt; TMenuItem UserMenu&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Load&amp;quot;&lt;/span&gt;, Task_Load&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;,
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Save&amp;quot;&lt;/span&gt;, Task_Save&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;,
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;View&amp;quot;&lt;/span&gt;, Task_View&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Edit&amp;quot;&lt;/span&gt;, Task_Edit&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Все эти задачи описываются как обычно, например:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task_Load &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Далее - одна тонкость. Чтобы компилятор правильно строил дерево вызовов подпрограмм, ему нужно указать, что функции, которые мы перечислили в массиве, являются задачами. Для этого в main() нужно для каждой такой функции вызвать сервис &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Task_Reserve:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; main &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    OS_Task_Reserve&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Task_Load&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Task_Reserve&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Task_Save&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Task_Reserve&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Task_View&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Task_Reserve&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Task_Edit&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;

После этого компилятор будет знать, что эти функции косвенно вызываются из main, и правильно распределит локальные переменные этих функций.
&lt;/p&gt;

&lt;p&gt;
Теперь в произвольном месте программы можно создавать задачи по указателю на функцию, пользуясь специальным сервисом &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Task_Create&lt;strong&gt;P&lt;/strong&gt;:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    LCD_Out&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;UserMenu&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;strMenu&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;        &lt;span class=&quot;co1&quot;&gt;// Выводим на экран название функции&lt;/span&gt;
    OS_Task_CreateP&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;, UserMenu&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;; &lt;span class=&quot;co1&quot;&gt;// Создаем задачу с высшим приоритетом&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;

(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#faq_по_осрв_osa&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Вернуться к списку FAQ&lt;/a&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Можно ли создать задачу по указателю на функцию?&quot; [15138-18111] --&gt;
&lt;h3&gt;&lt;a name=&quot;что_будет_если_отсылатьпринимать_сообщения_из_неинициализированной_очереди&quot; id=&quot;что_будет_если_отсылатьпринимать_сообщения_из_неинициализированной_очереди&quot;&gt;Что будет, если отсылать/принимать сообщения из неинициализированной очереди?&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

&lt;strong&gt;Ответ: ничего хорошего&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
Это относится не только к неинициализированной очереди, но и к неинициализированным: счетным семафорам, коротким сообщениям, указателям на сообщения. Неизвестно, что содержат в себе эти переменные на момент обращения к ним. Поэтому нужно всегда следить за тем, чтобы эти объекты ОС инициализировались &lt;strong&gt;до&lt;/strong&gt; первого обращения к ним.
&lt;/p&gt;

&lt;p&gt;
Рекомендую проанализировать работу следующего примера:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;OST_QUEUE q;
OST_MSG   smsg;
OST_MSG   rmsg;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task1 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    OS_Queue_Create&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;q&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;          &lt;span class=&quot;co1&quot;&gt;// Создаем очередь (инициализируем)&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        OS_Queue_Send&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;q, smsg&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;  &lt;span class=&quot;co1&quot;&gt;// Отсылаем сообщение в очередь&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task2 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        OS_Queue_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;q, rmsg&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;  &lt;span class=&quot;co1&quot;&gt;// Ожидаем сообщение из очереди&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; main &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    OS_Init&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Task_Create&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;, Task1&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Task_Create&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;, Task2&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Run&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Обратите внимание на расстановку приоритетов: приоритет задачи Task2 выше, чем Task1. Это означает, что первой выполнится именно задача Task2, т.е. та, которая ожидает сообщение из очереди, а задача, инициализирующая очередь, запустится второй (если &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Queue_Wait не вызовет сбой программы). При всей очевидности выхода из ситуации (а именно - правильной расстановке приоритетов), допустить такую ошибку очень просто. Для неприоритетного режима - вообще неизвестно, какая задача запустится первой. Поэтому очереди (и все остальные объекты, требующие инициализации) следует инициализировать так, чтобы на момент обращения к ним они гарантировано были инициализированны. Тривиальный способ - создавать их в функции main() до вызова сервиса &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Run().
&lt;/p&gt;

&lt;p&gt;

(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#faq_по_осрв_osa&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Вернуться к списку FAQ&lt;/a&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Что будет, если отсылать/принимать сообщения из неинициализированной очереди?&quot; [18112-21003] --&gt;
&lt;h3&gt;&lt;a name=&quot;изменение_типов_сообщений&quot; id=&quot;изменение_типов_сообщений&quot;&gt;Изменение типов сообщений&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

OSA имеет два вида сообщений: указатель на сообщение и короткое однобайтовое сообщение. Различаются они тем, что с помощью первого можно передавать любой объем информации, т.к. фактически передается только указатель на нее, а с помощью второго - только одно значение (по умолчанию это значения от 1 до 255). Учитывая архитектурные особенности PIC-контроллеров, программистам оставлена возможность изменять типы этих сообщений. 
&lt;/p&gt;

&lt;p&gt;
Сначала поговорим о типе &lt;strong&gt;указателя на сообщение&lt;/strong&gt;. По умолчанию указатель на сообщение имеет тип &lt;strong&gt;void* &lt;/strong&gt;, т.е. указатель на область RAM-памяти. Учитывая, что ядро PIC-контроллера построены по гарвардской архитектуре (раздельные шины адреса для памяти данных и программы), указатели на данные в ОЗУ и указатели на константы, хранящиеся в программной памяти, - это разные вещи. Поэтому ОСРВ OSA предоставляет программисту возможность на этапе написания программы выбрать тип указателей на сообщения. Этот тип нужно указать в файл конфигурации osacfg.h:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define OS_MSG_TYPE    const char *&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
в этом примере мы заменяем тип указателя на сообщение так, что сможем в программе обмениваться строковыми константами.

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;OST_MSG_CB  msg_cb;        &lt;span class=&quot;co1&quot;&gt;// Дескриптор сообщения&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt; MenuStrings&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Load&amp;quot;&lt;/span&gt;, &lt;span class=&quot;st0&quot;&gt;&amp;quot;Save&amp;quot;&lt;/span&gt;, &lt;span class=&quot;st0&quot;&gt;&amp;quot;Save as...&amp;quot;&lt;/span&gt;, &lt;span class=&quot;st0&quot;&gt;&amp;quot;Exit&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task_Menu &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; i &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;; i&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
            OS_Msg_Send&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;msg_cb, MenuString&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;em&gt;Примечание: в программе может быть применен только один тип для указателей на сообщения, и он не может меняться в ходе выполнения программы. (Исключение составляют указатели в HT-PICC18, когда указан ключ компиляции -CP24.)&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
Теперь об одной ошибке, вернее, о некоторой некорректности использования возможности замены типа указателя на сообщения. Несколько раз в программах видел такое:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;   name;
    &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;     age;
    &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;     weight;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; TMyStruct;
&amp;nbsp;
&lt;span class=&quot;co2&quot;&gt;#define OS_MSG_TYPE   TMyStruct *&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Т.е. программист создает некую свою структуру и тип сообщения заменяет указателем на нее. Ошибки здесь, конечно, нет. Но концептуально такой подход довольно спорный. За год программист может написать одну программу, а может и 10, и 20. Каждая программа может быть индивидуальна, и данные, которыми будут обмениваться задачи, - тоже. Если в каждой программе подменять тип указателя на сообщение каким-то специфичным указателем вместо void*, то возникнет некоторая неразбериха, да и проблемы с переносом модулей. Если предполагается работать с указателями на эти структуры, расположенные в RAM-области памяти, то лучше оставить тип void*. Это же замечание касается указателей на структуры, расположенные в ROM-области памяти - их лучше определять как &lt;strong&gt;const void* &lt;/strong&gt;.
&lt;/p&gt;

&lt;p&gt;

Теперь два слова о изменении типа &lt;strong&gt;короткого сообщения&lt;/strong&gt;. Для чего оно вообще сделано? Две причины: экономия RAM и повышение скорости. Довольно часто между программами нужно обмениваться незначительными объемами информации: &amp;quot;нажата кнопка 5&amp;quot;, &amp;quot;переключиться в режим 3&amp;quot;, &amp;quot;зажечь светодиод 12&amp;quot;. Преимущества перед указателями на сообщения:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; если бы мы использовали для передачи такой информации указатели на сообщения, нам бы пришлось тратить 2 байта ОЗУ (3 - для PIC18, 4 для PIC24 и dsPIC) на дескриптор сообщения и 1 байт на тело сообщения, куда будет указывать дескриптор, итого - 3 байта. При использовании короткого сообщения нам понадобится всего 1 байт. &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; при приеме указателя на сообщение программе потребуется сперва сформировать указатель в FSR, а только потом уже получить доступ к информации. При приеме же короткого сообщения мы получаем информацию напрямую.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; для указателей на сообщения нельзя менять содержимое отправленных данных, пока задача-приемник не получила сообщение. При использовании короткого сообщения это ограничение снимается, т.к. все сообщение помещается в дескриптор.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

По умолчанию короткое сообщение имеет тип &lt;strong&gt;unsigned char&lt;/strong&gt;. Этот тип может быть заменен на любой перечислимый тип (int, long, float, bit) заданием константы в файле osacfg.h:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define OS_SMSG_TYPE   unsigned long&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Такое, хоть и редко, но бывает нужно. 
&lt;/p&gt;

&lt;p&gt;
У короткого сообщения есть две особенности, которые нужно учитывать при проектировании программы: 
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; это сообщение может иметь только перечислимый тип (нужно учитывать при замене типа); т.е. этот тип нельзя переопределить на структуру, поскольку запись значения в короткое сообщение и чтение из него выполняется оператором присваивания ( = );&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; через него нельзя передавать нулевое значение. Этим приходится платить за использование такого эффективного инструмента. Нулевое значение рассматривается системой как отсутствие сообщения.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#faq_по_осрв_osa&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Вернуться к списку FAQ&lt;/a&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Изменение типов сообщений&quot; [21004-29097] --&gt;
&lt;h3&gt;&lt;a name=&quot;если_две_задачи_ожидают_одного_и_того_же_события&quot; id=&quot;если_две_задачи_ожидают_одного_и_того_же_события&quot;&gt;Если две задачи ожидают одного и того же события.&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

При написании программы с использованием ОСРВ OSA нужно учитывать одну особенность ее планировщика. При поиске лучшей готовой задачи для выполнения планировщик в цикле пробегается по всем дескрипторам, проверяет готовность задач и сравнивает их приоритеты. Управление получит готовая задача с высшим приоритетом. Если есть несколько задач с одинаковым приоритетом, то управление получит та, которая была рассмотрена планировщиком раньше. Дескрипторы задач хранятся в массиве, который планировщиком рассматривается как кольцевой. Каждый раз он начинает поиск готовой задачи со следующей после последней выполненной. Например, у нас 5 задач. Последней задачей выполнялась 3-я. Тогда порядок проверки задач при следующей работе планировщика будет таким: 4, 5, 1, 2, 3. 
&lt;/p&gt;

&lt;p&gt;
Проблема в том, что если две задачи ждут одного и того же события, например, семафора, который устанавливается третьей задачей, то одна из задач никогда не получит управление. Рассмотрим пример:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Задача, устанавливающая семафор&lt;/span&gt;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task1 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        OS_Bsem_Set&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;bsem&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        OS_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Первая задача, ожидающая семафор&lt;/span&gt;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task2 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        OS_Bsem_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;bsem&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Вторая задача, ожидающая семафор&lt;/span&gt;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task3 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        OS_Bsem_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;bsem&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;coMULTI&quot;&gt;/******************************************************************************/&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; main &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    OS_Init&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    OS_Task_Create&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;, Task1&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;   &lt;span class=&quot;co1&quot;&gt;// Все задачи с равными приоритетами&lt;/span&gt;
    OS_Task_Create&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;, Task2&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Task_Create&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;, Task3&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    OS_Run&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Последовательность просмотра задач планировщиком будет такова: Task1, Task2, Task3, Task1, Task2, Task3, … . Проблема в том, что Task2 и Task3 не получают управления до тех пор, пока не будет установлен двоичный семафор, а устанавливается он только в Task1. Поэтому всегда будет происходить одна и та же последовательность: 
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Task1&lt;/strong&gt; устанавливает семафор &lt;strong&gt;bs&lt;/strong&gt;;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Task2&lt;/strong&gt; и &lt;strong&gt;Task3&lt;/strong&gt; становятся готовыми к выполнению;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; после работы &lt;strong&gt;Task1&lt;/strong&gt; планировщик начинает поиск задачи, начиная со следующей после последней выполненной, т.е. начиная с &lt;strong&gt;Task2&lt;/strong&gt;;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; планировщик рассматривает &lt;strong&gt;Task2&lt;/strong&gt;, видит, что она готова; лучший приоритет пока не выбран, поэтому лучшим на данный момент планировщик будет считать 1 (приоритет задачи Task2). Планировщик ее запоминает как кандидата на запуск;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; планировщик рассматривает &lt;strong&gt;Task3&lt;/strong&gt;, видит, что она готова, но ее приоритет не лучше, чем текущий лучший, поэтому он эту задачу пропускает;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; планировщик рассматривает &lt;strong&gt;Task1&lt;/strong&gt;, видит, что она не готова к выполнению (она в задержке);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; управление передается задаче &lt;strong&gt;Task2&lt;/strong&gt;;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;Task2&lt;/strong&gt; сбрасывает семафор (сервис &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Bsem_Wait делает это автоматически), отрабатывает какие-то свои действия и возвращается в планировщик;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; планировщик начинает просмотр списка задач, начиная с &lt;strong&gt;Task3&lt;/strong&gt;, но уже видит, что семафор сброшен, поэтому &lt;strong&gt;Task3&lt;/strong&gt; опять ставится в ожидание;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; следующей управление получит &lt;strong&gt;Task1&lt;/strong&gt; по истечении времени задержки, а потом все начнется сначала.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Как тут быть? Единого способа решения нет. Можно ретранслировать семафор дальше, т.е. дождавшись его в Task2, сразу же установить его, чтобы и Task3 смогла получить управление. Но это не лучший вариант, т.к. неизвестно, сколько задач в цепочке, и на какой нужно останавливать установку семафора. Можно использовать счетный семафор, но опять же нужно знать, сколько задач его ожидают, чтобы установить в нем правильное число. На мой взгляд, самым удобным в таком случае будет использование флагов. В задаче-отправителе устанавливать все биты флага:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    OS_Flag_Set_1&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;flag, &lt;span class=&quot;nu12&quot;&gt;0xFF&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
а в задачах-приемниках ожидать только своего.

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;coMULTI&quot;&gt;/* В задаче Task2 */&lt;/span&gt;
    OS_Flag_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;flag, &lt;span class=&quot;nu12&quot;&gt;0x01&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Flag_Set_0&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;flag, &lt;span class=&quot;nu12&quot;&gt;0x01&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;coMULTI&quot;&gt;/* В задаче Task3 */&lt;/span&gt;
    OS_Flag_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;flag, &lt;span class=&quot;nu12&quot;&gt;0x02&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Flag_Set_0&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;flag, &lt;span class=&quot;nu12&quot;&gt;0x02&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
В общем, способы решения есть, а какой применять, - на усмотрение программиста. &lt;strong&gt;Главное - надо помнить об этой особенности планировщика.&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;

(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#faq_по_осрв_osa&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Вернуться к списку FAQ&lt;/a&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Если две задачи ожидают одного и того же события.&quot; [29098-36508] --&gt;
&lt;h3&gt;&lt;a name=&quot;модификация_тела_сообщения_до_того_как_оно_будет_принято&quot; id=&quot;модификация_тела_сообщения_до_того_как_оно_будет_принято&quot;&gt;Модификация тела сообщения до того, как оно будет принято&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

В одной присланной мне программе была допущена такая ошибка: задача формировала тело сообщения, отправляла другой задаче указатель на него, а потом сразу же модифицировала для отправки следующего. Выглядело это так:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;OST_MSG_CB msg_cb;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
       &lt;span class=&quot;co1&quot;&gt;// Формируем первое сообщение&lt;/span&gt;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'1'&lt;/span&gt;;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'2'&lt;/span&gt;;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'3'&lt;/span&gt;;
       OS_Msg_Send&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;msg_cb, buf&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
       OS_Yield&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
       &lt;span class=&quot;co1&quot;&gt;// Формируем второе сообщение&lt;/span&gt;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'5'&lt;/span&gt;;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'6'&lt;/span&gt;;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'7'&lt;/span&gt;;
       OS_Msg_Send&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;msg_cb, buf&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
       OS_Yield&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
       &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
   &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Не смотря на то, что после отправки сообщения выполняется &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Yield, чтобы дать возможность адресату получить сообщение, оно может и не доставиться с первого раза (по любой причине: задача-приемник чем-то занята, или имеет низкий приоритет, или, наконец, находится в режиме паузы). Сервис &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Msg_Send устроен так, что он не сможет отправить сообщение до тех пор, пока предыдущее не получено адресатом. Тем не менее, следует помнить, что этот сервис следит только за дескриптором сообщения, а не за областью памяти, где фактически расположено тело. 
&lt;/p&gt;

&lt;p&gt;
Что произойдет в нашем примере, если после первого &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Yield задача-адресат не успеет принять отправленное ей сообщение? Фактически ей отправляется только адрес области памяти, где располагается массив buf, в котором на момент отправки лежат значения &amp;#039;1&amp;#039;, &amp;#039;2&amp;#039; и &amp;#039;3&amp;#039;. Итак, после выполнения &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Yield сообщение не было принято. Планировщик возвращает управление задаче отправителю, и она продолжает свое выполнение с того места, откуда вышла, т.е. со следующей строки после &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Yield. Здесь у нас происходит замена элементов буфера buf на &amp;#039;5&amp;#039;, &amp;#039;6&amp;#039; и &amp;#039;7&amp;#039;. После этого задача Task пытается отправить очередное сообщение, но так как предыдущее еще не получено адресатом, то эта задача становится в ожидание, когда дескриптор освободится. 
&lt;/p&gt;

&lt;p&gt;
По прошествии какого-то времени адресат получит сообщение и освободит дескриптор, но в теле сообщения будут уже новые значения (не &amp;quot;123&amp;quot;, а &amp;quot;567&amp;quot;). Задача отправитель же, дождавшись освобождения дескриптора, отсылает следующее сообщение; фактически - это тот же указатель на тот же участок памяти. И задача-адресат второй раз подряд получит &amp;quot;567&amp;quot;.
&lt;/p&gt;

&lt;p&gt;
Как этого избежать? Всего-навсего перед формированием нового сообщения в том же буфере нужно проверять, было ли предыдущее сообщение доставлено.

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;       &lt;span class=&quot;co1&quot;&gt;// Формируем первое сообщение&lt;/span&gt;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'1'&lt;/span&gt;;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'2'&lt;/span&gt;;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'3'&lt;/span&gt;;
       OS_Msg_Send&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;msg_cb, buf&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
       OS_Cond_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_Msg_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;msg_cb&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;   &lt;span class=&quot;co1&quot;&gt;// Ставим задачу в режим ожидания до тех пор,&lt;/span&gt;
                                              &lt;span class=&quot;co1&quot;&gt;// пока сообщение не будет принято&lt;/span&gt;
&amp;nbsp;
       &lt;span class=&quot;co1&quot;&gt;// Формируем второе сообщение&lt;/span&gt;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'5'&lt;/span&gt;;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'6'&lt;/span&gt;;
       buf&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'7'&lt;/span&gt;;
       OS_Msg_Send&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;msg_cb, buf&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
       OS_Yield&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;

(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#faq_по_осрв_osa&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Вернуться к списку FAQ&lt;/a&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Модификация тела сообщения до того, как оно будет принято&quot; [36509-41342] --&gt;
&lt;h3&gt;&lt;a name=&quot;использование_таймеров_вне_задач&quot; id=&quot;использование_таймеров_вне_задач&quot;&gt;Использование таймеров вне задач&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Как статические, так и динамические таймеры могут использоваться в любом месте программы (за исключением сервисов Wait и Delay). Это позволяет:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;1. В любом месте программы ожидать какого-то условия с выходом по таймауту.&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; MyFunc1 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    OS_Stimer_Run&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                  &lt;span class=&quot;co1&quot;&gt;// Запускаем статический таймер 0&lt;/span&gt;
                                           &lt;span class=&quot;co1&quot;&gt;// на отсчет 10 системных тиков&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;RB0 &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_Stimer_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;    &lt;span class=&quot;co1&quot;&gt;// Ожидаем установки RB0&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;continue&lt;/span&gt;;
    &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;

&lt;strong&gt;2. Формировать задержку внутри фоновых функций (не задач).&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; MyFunc2 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    OS_Stimer_Run&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                  &lt;span class=&quot;co1&quot;&gt;// Запускаем статический таймер 0&lt;/span&gt;
                                           &lt;span class=&quot;co1&quot;&gt;// на отсчет 10 системных тиков&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_Stimer_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;continue&lt;/span&gt;;  &lt;span class=&quot;co1&quot;&gt;// Ожидаем конца счета&lt;/span&gt;
    &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;strong&gt;3. Выделять квант времени для работы какой-либо функции.&lt;/strong&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; buffer&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; OST_DTIMER dt;
&amp;nbsp;
    OS_Dtimer_Create&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;dt&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                   &lt;span class=&quot;co1&quot;&gt;// Инициализируем таймер&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        OS_Dtimer_Run&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;dt, &lt;span class=&quot;nu0&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;              &lt;span class=&quot;co1&quot;&gt;// Выделяем для функции Receive квант&lt;/span&gt;
                                            &lt;span class=&quot;co1&quot;&gt;// времени в 50 тиков&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Receive&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;dt&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
            &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/*...*/&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; Receive &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;OST_DTIMER &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;dt&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; bit b;
    &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        b &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; GetBit&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                       &lt;span class=&quot;co1&quot;&gt;// Принимаем бит данных&lt;/span&gt;
        ShiftBit&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;b&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                        &lt;span class=&quot;co1&quot;&gt;// Вдвигаем его в буфер&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;CheckSumOK&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;         &lt;span class=&quot;co1&quot;&gt;// Проверяем контрольную сумму&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_Dtimer_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;dt&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;        &lt;span class=&quot;co1&quot;&gt;// Висим в цикле, пока таймер не досчитает&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;co1&quot;&gt;// Попали сюда, значит ничего не приняли за отведенное время&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#faq_по_осрв_osa&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Вернуться к списку FAQ&lt;/a&gt;)
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Использование таймеров вне задач&quot; [41343-43950] --&gt;
&lt;h3&gt;&lt;a name=&quot;почему_виснет_os_delay&quot; id=&quot;почему_виснет_os_delay&quot;&gt;Почему виснет OS_Delay?&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

&amp;quot;Почему после вызова &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay() задача подвисает, значение таймеров не изменяется, хотя в osacfg.h определена константа &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_ENABLE_TTIMERS?&amp;quot;. Как ни странно, вопрос довольно частный.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Ответ: счет таймеров выполняет сервис &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer, который должен периодически вызываться.&lt;/strong&gt;
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Почему виснет OS_Delay?&quot; [43951-] --&gt;</description>
    </item>
    <item rdf:about="http://www.pic24.ru/doku.php/osa/articles/rtos_usage_4?rev=1266955996">
        <dc:format>text/html</dc:format>
        <dc:date>2010-02-23T23:13:16+03:00</dc:date>
        <title>Советы по оптимизации</title>
        <link>http://www.pic24.ru/doku.php/osa/articles/rtos_usage_4?rev=1266955996</link>
        <description>


&lt;h2&gt;&lt;a name=&quot;советы_по_оптимизации&quot; id=&quot;советы_по_оптимизации&quot;&gt;Советы по оптимизации&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Здесь будут описаны правила, советы и рекомендации по конфигурированию OSA, а также по применению сервисов OSA с целью увеличения эффективности использования ресурсов микроконтроллера. Есть три ресурса, которые хотелось бы сэкономить: RAM, ROM и время. Сначала я предполагал разбить этот раздел на три части, посвятив каждую из них своему параметру. Однако, получилось так, что приемы и советы по экономии RAM и ROM практически идентичны, поэтому им будет посвящена одна часть.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;#оптимизация_памяти&quot; title=&quot;osa:articles:rtos_usage_4 &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Оптимизация памяти&lt;/a&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#отключение_приоритетов&quot; title=&quot;osa:articles:rtos_usage_4 &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Отключение приоритетов&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#размерность_таймеров&quot; title=&quot;osa:articles:rtos_usage_4 &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Размерность таймеров&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#замена_таймеров_задач_статическими_таймерами&quot; title=&quot;osa:articles:rtos_usage_4 &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Замена таймеров задач статическими таймерами&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#os_timer_inline&quot; title=&quot;osa:articles:rtos_usage_4 &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;OS_Timer inline&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#размещение_переменных&quot; title=&quot;osa:articles:rtos_usage_4 &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Размещение переменных&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#отправка_сообщений&quot; title=&quot;osa:articles:rtos_usage_4 &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Отправка сообщений&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#очереди_сообщений&quot; title=&quot;osa:articles:rtos_usage_4 &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Очереди сообщений&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
&lt;a href=&quot;#оптимизация_скорости&quot; title=&quot;osa:articles:rtos_usage_4 &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Оптимизация скорости&lt;/a&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#выбор_приоритетов&quot; title=&quot;osa:articles:rtos_usage_4 &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Выбор приоритетов&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#полное_отключение_приоритетов&quot; title=&quot;osa:articles:rtos_usage_4 &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Отключение приоритетов&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#замена_ожидания_на_задержку&quot; title=&quot;osa:articles:rtos_usage_4 &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Замена ожидания на задержку&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#проверка_условия_перед_ожиданием&quot; title=&quot;osa:articles:rtos_usage_4 &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Проверка условия перед ожиданием&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Советы по оптимизации&quot; [1-2170] --&gt;
&lt;h3&gt;&lt;a name=&quot;оптимизация_памяти&quot; id=&quot;оптимизация_памяти&quot;&gt;Оптимизация памяти&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Оптимизация памяти в основном производится за счет правильного конфигурирования, но есть и приемы, относящиеся к использованию сервисов. Начальная конфигурация по умолчанию (когда файл osacfg.h пустой) подобрана так, чтобы обеспечить программиста всем необходимым, не давая ничего лишнего. Но каждая программа индивидуальна и есть смысл для каждой подбирать оптимальную конфигурацию.
&lt;/p&gt;

&lt;p&gt;
(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;отключение_приоритетов&quot; id=&quot;отключение_приоритетов&quot;&gt;Отключение приоритетов&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Во многих приложениях отключение приоритетности - очень полезная вещь. Как было описано выше (в разделе &lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#как_расставлять_приоритеты&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Как расставлять приоритеты&lt;/a&gt;), в большинстве приложений под кооперативной ОСРВ приоритетность не нужна. Когда мы отключаем приоритетность, код планировщика сильно упрощается и сокращается, т.к. он больше не содержит цикла предварительного перебора всех задач в поиске готовых и кода сравнения приоритетов. Кроме того, из прораммы выкидываются системные переменные, содержащие максимальный приоритет, адрес задачи с максимальным приоритетом и адрес последней выполненной задачи. Т.е., отключив приоритеты, мы экономим и RAM, и ROM, и время работы планировщика (см. &lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#отключение_приоритетов&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;ниже&lt;/a&gt;).
&lt;/p&gt;

&lt;p&gt;
По умолчанию приоритетность включена. Отключить ее можно, задав в файле osacfg.h константу:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define OS_DISABLE_PRIORITY&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;размерность_таймеров&quot; id=&quot;размерность_таймеров&quot;&gt;Размерность таймеров&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Таймеры в OSA - очень гибкий в настройке инструмент. Помимо того, что есть три типа таймеров: таймеры задач, пользовательские статические и пользовательские динамические таймеры, - каждый из которых обладает своими преимуществами, есть еще возможность выбирать размерность каждого типа таймера в зависимости от возложенных на него задач.
&lt;/p&gt;

&lt;p&gt;
&lt;em&gt;(&lt;strong&gt;Примечание.&lt;/strong&gt; Есть еще четвертый тип таймеров, исторически сложилось так, что называется он &amp;quot;статические таймеры старого типа&amp;quot;, но т.к. эти таймеры были довольно сложны в настройке, от их дальнейшего сопровождения пришлось отказаться, хотя в системе они остались и функционируют. См. приложение к документации)&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
По умолчанию размерность таймера равна двум байтам. Это покрывает большую часть потребностей в организации задержек в программах. Такие таймеры могут отсчитывать до 65535 системных тиков (периодов вызова &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer). Больше обычно не нужно. Но зато часто встречаются случаи, когда все задержки (и ожидания событий с таймаутами) не длиннее 255 тиков. Например, &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer вызывается в обработчике прерывания по TMR2, настроенного так, чтобы его период был 10 мс. В этом случае, используя отсчет таймера в 255 тиков, мы получаем задержку в 2.5 секунды. Для многих приложений этого более чем достаточно. 
&lt;/p&gt;

&lt;p&gt;
Поэтому во многих случаях есть смысл таймеры задач сделать однобайтовыми. Делается это заданием размерности таймера в файле osacfg.h:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define OS_TTIMER_SIZE      1&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Что мы выигрываем, уменьшив размерность таймеров задач? 
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Во-первых, функция инициализации таймера в своих параметрах будет получать не 2 байта, а один (кто знаком со внутренней организацией HT-PICC, знают, что однобайтовый параметр передается напрямую через WREG, в то время как двухбайтовый требует для передачи дополнительную ячейку памяти);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Во-вторых, сильно сокращается код функции &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer(). Дело в том, что эта функция для сокращения размера кода и скорости работы увеличивает все таймеры не в цикле, а по очереди напрямую. Поэтому уменьшение размерности таймера задач уменьшит размер функции пропорционально количеству задач (чем больше задач, тем сильнее сократится &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; В-третьих, все дескрипторы задач станут на один байт меньше (при 10 задачах в системе уже экономия в 10 байт, а для PIC16 это немало).&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;em&gt;&lt;strong&gt;Примечание 1.&lt;/strong&gt; Размерность можно также менять и у статических, и у динамических таймеров.&lt;/em&gt;&lt;br/&gt;
 
&lt;em&gt;&lt;strong&gt;Примечание 2.&lt;/strong&gt; Для 16-разрядных контроллеров (PIC24 и dsPIC) уменьшение размерности таймера до одного байта  не приведет к сокращению кода или экономии RAM.&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;замена_таймеров_задач_статическими_таймерами&quot; id=&quot;замена_таймеров_задач_статическими_таймерами&quot;&gt;Замена таймеров задач статическими таймерами&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Иногда при дефиците ресурсов может оказаться полезным воспользоваться статическими таймерами, вместо таймеров задач. При включении таймеров задач (включаются определением константы &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_ENABLE_TTIMERS) появляется возможность использовать в задачах задержки (&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay) и ожидание событий с выходом по таймауту (&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_xxx_Wait_TO). За каждым дескриптором задачи при этом закрепляется свой таймер (так называемый таймер задач), и их будет столько, сколько дескрипторов зарезервировано в osacfg.h (константа &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_TASKS). Получается, что если у нас 10 задач, а таймеры используются, например, только в трех, то мы имеем в памяти 7 переменных, которые не используются, но занимают место. 
&lt;/p&gt;

&lt;p&gt;
В таких случаях имеет смысл отключить таймеры задач и воспользоваться статическими таймерами. Для нашего примера мы определяем константу в osacfg.h:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define OS_STIMERS     3&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
И теперь у нас только 3 ячейки памяти занято таймерами, а мы сэкономили 7 ячеек. При двухбайтовых таймерах это 14 байт оперативной памяти. Теперь задержки в задачах будут выглядеть так:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    OS_Stimer_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;ST_ID, &lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;

где ST_ID - номер статического таймера (от 0 до 2 в нашем примере).
&lt;/p&gt;

&lt;p&gt;
Как заменить сервисы ожидания с выходом по таймауту, если все сервисы &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_xxx_Wait_TO требуют наличия таймеров задач? Очень просто, воспользовавшись сервисом &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Cond_Wait, который позволяет ожидать в задаче &lt;strong&gt;любое&lt;/strong&gt; условие. Например, мы ждем сообщение из очереди:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;co1&quot;&gt;// С таймерами задач&lt;/span&gt;
    OS_Queue_Wait_TO&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;queue, msg, &lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_IsTimeout&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;coMULTI&quot;&gt;/* обрабатываем сообщение */&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;co1&quot;&gt;// Без таймеров задач, но со статическими таймерами&lt;/span&gt;
    OS_Stimer_Run&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    OS_Cond_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;OS_Queue_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;queue&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_Stimer_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_Stimer_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        OS_Queue_Accept&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;queue, msg&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;coMULTI&quot;&gt;/* обрабатываем сообщение */&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
При использовании статического таймера запись оказалась более громоздкой, тем не менее сгенерированный код будет примерно одинаковым по размеру.
&lt;/p&gt;

&lt;p&gt;
Итак, что мы выигрываем от замены таймеров задач статическими таймерами?
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; В памяти не создаются таймеры для тех задач, которые их не используют;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Сокращается обработчик &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer (т.к. он должен пробегаться по всему списку таймеров задач, даже по неиспользуемым);&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Теряем мы удобство и наглядность.
&lt;/p&gt;

&lt;p&gt;
Еще одно применение статических таймеров можно рассмотреть в контексте предыдущего параграфа (&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#размерность_таймеров&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;Размерность таймеров&lt;/a&gt;). Там мы указали, что часто бывает так, что задержки в задачах не превышают 255 системных тиков. Но бывает и так, что в 9-ти задачах не превышают, а 10-ой, хоть тресни, нужен 32-разрядный таймер. Понятно, что из-за одной задачи не хотелось бы всем приделывать 4-байтовые таймеры. Поэтому удобно организовать один 4-байтовый статический таймер для этой задачи, а всем остальным определить 1-байтовые. Вот фрагмент файла osacfg.h:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define OS_ENABLE_TTIMERS          // Разрешаем использование таймеров задач&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define OS_TTIMER_SIZE      1      // Размерность таймеров задач&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define OS_STIMERS          1      // Определяем один статический таймер&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define OS_STIMER_SIZE      4      // Размерность статического таймера&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;os_timer_inline&quot; id=&quot;os_timer_inline&quot;&gt;OS_Timer inline&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Сервис &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer вызывает системную функции _&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer(). Учитывая, что чаще всего вызов этого сервиса производят по переполнению какого-либо таймера (TMR0, TMR1, TMR2), обычно он располагается внутри функции-прерывания. Компиляторы от HTSoft в таком случае ведут себя следующим образом: т.к. сама функция _&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer() относительно функции прерывания находится во внешнем другом модуле, то функция прерывания ничего не знает о ресурсах, которые использует _&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer(), поэтому при входе в прерывание сохраняются все критичные регистры, которые, по предположению компилятора, может изменить внешняя функция. К ним относятся: все FSR, пара PRODH:PROLH, 15 регистров btemp, и тройка регистров TABLAT, TBLPTRH:TBLPTRL. Возможно в самом прерывании они и не используются, а все прерывание выглядит так:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; interrupt myisr &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    OS_EnterInt&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;TMR2IF&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        TMR2IF &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
        OS_Timer&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
    OS_LeaveInt&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Но на этапе компиляции файла, содержащего код прерывания, компилятор не знает, что творится внутри функции, вызываемой сервисом &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer. Сохранение/восстановление при входе/выходе из прерывания занимает более 100 слов памяти ROM и, соответственно, более 100 машинных циклов. Т.е. если у нас таймер запрограммирован так, чтобы прерывание происходило каждые 256 циклов, то более 40% всего процессорного времени будет тратиться только на вход и выход из прерывания. 
&lt;/p&gt;

&lt;p&gt;
В ОСРВ OSA есть возможность вместо вызова функции _&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer подставлять ее тело напрямую. Для этого нужно определить константу в файле osacfg.h:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define OS_USE_INLINE_TIMER&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;strong&gt;Ограничение:&lt;/strong&gt; если эта константа определена, то в программе &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer() может вызываться в единственном месте. Возможно, в дальнейшем это ограничение будет снято.
&lt;/p&gt;

&lt;p&gt;
(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;размещение_переменных&quot; id=&quot;размещение_переменных&quot;&gt;Размещение переменных&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

(Этот параграф имеет отношение не столько к OSA, сколько к PIC18)
&lt;/p&gt;

&lt;p&gt;
Для PIC18 некоторый выигрыш по программной памяти даст размещение переменных в ACCESS-банке (первые 128 или 96 байт RAM-памяти). Обращение к таким переменным производится без предварительной установки регистра BSR, следовательно, при частом обращении к ним можно сэкономить на всех инструкциях movlb. Свои переменные можно размещать в этом банке вручную. Например, для HT-PICC18:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;near &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; a;
near &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;  i;&lt;/pre&gt;
&lt;p&gt;

для MCC18:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#pragma udata access my_vars&lt;/span&gt;
near &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; a;
near &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;  i;&lt;/pre&gt;
&lt;p&gt;
В ОСРВ OSA также есть возможность размещать все внутренние переменные в разных банках: дескрипторы задач, системные переменные, статический таймеры, бинарные семафоры. Для каждого типа этих данных можно указать свой банк (0 - access, 1 - остальная память):

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define OS_BANK_OS          0         // Размещение системных переменных&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define OS_BANK_TASKS       1         // Размещение дескрипторов задач&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define OS_BANK_BSEMS       0         // Размещение двоичных семафоров&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#define OS_BANK_OS          1         // Размещение статических таймеров&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;)
&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;отправка_сообщений&quot; id=&quot;отправка_сообщений&quot;&gt;Отправка сообщений&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Обычно сообщения отправляются сервисом &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Msg_Send. Как этот сервис работает? Предварительно он проверяет, занят дескриптор сообщения или свободен (т.е. обработано ли предыдущее сообщение или еще нет). Если дескриптор занят, то задача становится в режим ожидания и передает управление планировщику. Иногда бывает так, что не принципиально, обработалось предыдущее сообщение или нет. Например, задача измеряет напряжение бортовой сети автомобиля и сообщением отправляет его головной задаче. Головной задаче не важна последовательность изменений напряжения, ей нужно текущее значение. Поэтому иногда нет смысла выполнять лишние операции по проверки занятости дескриптора, по формированию адреса возврата, да еще задержки на неопределенное время. Для отправки сообщения, не дожидаясь освобождения дескриптора (т.е. фактически для перетирания старого тела сообщения новым), можно пользоваться сервисом:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    OS_Msg_Send_Now&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;msg_cb, msg&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Например, для PIC16 этот сервис занимает 7 слов программной памяти, в то время как &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Msg_Send требует 17 слов.
&lt;/p&gt;

&lt;p&gt;
&lt;em&gt;&lt;strong&gt;Примечание&lt;/strong&gt;. Это же относится к отправке коротких сообщений, а также к отправке сообщений и коротких сообщений в очередь.&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;)
&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;очереди_сообщений&quot; id=&quot;очереди_сообщений&quot;&gt;Очереди сообщений&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

OSA предоставляет возможность программисту использовать два типа очередей в своей программе, причем их можно использовать одновременно, т.к. они независимы друг от друга. Однако, их независимость порождает небольшую проблему: в программе присутствуют отдельные функции по добавлению/извлечению сообщения для обоих типов очередей. Т.е. в программной памяти два экземпляра функции _&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Queue_Send и два экземпляра функции _&lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Queue_Accept. Это позволяет программисту использовать в своей программе сообщения разных типов и размерностей. Но если в программе используются сообщения одинаковых размерностей? Т.е. размерность указателя на сообщения такая же, как размерность короткого сообщения (для PIC16, например, указатель на RAM однобайтовый). В этом случае использование двух экземпляров функций неоправданно. 
&lt;/p&gt;

&lt;p&gt;
В таких случаях программисту предоставляется возможность объединить эти функции, т.е., вернее, использовать одни и те же функции для сообщений разных типов. Для этого нужно в файле osacfg.h определить константу:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#define OS_QUEUE_SQUEUE_IDENTICAL&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Так мы сэкономим около 100 слов программной памяти. Однако после применения такого приема следует помнить, что был сделан допуск относительно размерности сообщений. И если вдруг нужно будет поменять тип одного из сообщений (указателя на сообщение или короткого сообщения), то следует пересмотреть возможность использования одной функции для работы с очередями этих сообщений.
&lt;/p&gt;

&lt;p&gt;
(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Оптимизация памяти&quot; [2171-25081] --&gt;
&lt;h3&gt;&lt;a name=&quot;оптимизация_скорости&quot; id=&quot;оптимизация_скорости&quot;&gt;Оптимизация скорости&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;выбор_приоритетов&quot; id=&quot;выбор_приоритетов&quot;&gt;Выбор приоритетов&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Давайте еще раз рассмотрим принцип работы планировщика в ОСРВ OSA. Планировщик по очереди перебирает все активные (созданные) задачи, проверяя их готовность. Одновременно он сравнивает приоритеты у всех готовых задач, для чего лучший из приоритетов, а также адрес задачи с лучшим приоритетам сохраняет в своих внутренних переменных. После прохода всех задач управление передается той, чей адрес сохранен во внутренней переменной, т.е. чей приоритет был высшим из всех готовых задач.
&lt;/p&gt;

&lt;p&gt;
Если во время проверки встречаются несколько готовых задач с одинаковым приоритетом, то управление получит та из них, которая была проверена первой. Получается так, что если планировщик натыкается на готовую задачу с высшим приоритетом (0), то дальнейший поиск делать бессмысленно, поскольку, даже если и найдутся еще готовые задачи с высшим приоритетом, планировщик все равно запустит ту, которая была найдена первой. Планировщик OSA пользуется этим моментом и, находя готовую задачу с высшим приоритетом, сразу же прерывает поиск и передает управление найденной задаче.
&lt;/p&gt;

&lt;p&gt;
Учитывая, что полное время проверки готовности одной задачи (с выбором адреса задачи, с передачей ей управления для проверки готовности, с самой проверкой и с возвратом в планировщик) длится около 70 тактов. И если у нас 10 задач, то общий поиск будет длиться около 700 тактов.
Если же присутствует задача с нулевым приоритетом, то в случае ее готовности время работы планировщика сократится в среднем вдвое (это время будет колебаться от 70 до 700 тактов в зависимости от того, с какой задачи планировщик начинает просмотр).
&lt;/p&gt;

&lt;p&gt;
Итак, рекомендация такова: если используется приоритетный режим, то желательно самым приоритетным задачам устанавливать именно высший (а не просто самый высокий) приоритет, т.к. это ускорит работу планировщика.
&lt;/p&gt;

&lt;p&gt;
(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;полное_отключение_приоритетов&quot; id=&quot;полное_отключение_приоритетов&quot;&gt;Полное отключение приоритетов&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Об этом уже разговор был. Постольку, поскольку при отключении приоритетности убирается код, занимающийся предварительным перебором задач и сравнением приоритетов, то работа планировщика ускорится в &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_TASKS раз.
&lt;/p&gt;

&lt;p&gt;
(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;замена_ожидания_на_задержку&quot; id=&quot;замена_ожидания_на_задержку&quot;&gt;Замена ожидания на задержку&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Два слова о том, как проверяется условие при ожидании. Например, задача ожидает установку двоичного семафора. Что при этом происходит:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; задача переводит себя в режим ожидания (сбрасывает себе флаг bReady);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; задача сохраняет текущее значение программного счетчика;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; задача выполняет проверку семафора;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; если семафор установлен, то задача переводит себя в режим готовности (устанавливает флаг bReady);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; задача возвращает управление планировщику; (обратите внимание, что возврат в планировщик произойдет при любом значении семафора, т.к. среди задач может оказаться более приоритетная, которая ожидает тот же семафор);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; планировщик, проверив все остальные задачи, доходит до нашей;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; планировщик передает управление на тот адрес, который задача сохранила в шаге 2 (при этом мы попадаем в задачу как раз в то место, где производится проверка семафора);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; задача выполняет повторную проверку семафора (он за это время мог быть сброшен более приоритетной задачей);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; если семафор уже сброшен, то задача переводит себя в режим ожидания (сбрасывает флаг bReady);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; если семафор установлен и задача готова к выполнению, то она идет выполнять дальнейший код (который следует за ожиданием семафора);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; если семафор установлен, но задача еще в режиме ожидания, то идем на шаг 4.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Т.е. &lt;strong&gt;при ожидании события планировщик всегда заглядывает внутрь задачи&lt;/strong&gt; (передает ей управление, чтобы она сама проверила свое условие). Это требует времени. Если семафор устанавливается довольно редко, а скорость реакции на него некритична (например, в будильнике, по которому мы просыпаемся на работу не так важно, зазвенит он в 07:00:00 или в 07:00:05), то очень много тактов процессора тратится впустую.
&lt;/p&gt;

&lt;p&gt;
Теперь рассмотрим поведение планировщика, когда какая-либо задача находится в ожидании задержки &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay. Что происходит, когда задача выполняет &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay(100):
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; таймер в дескрипторе задачи инициализируется указанным значением (в нашем примере 100);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; задача устанавливает себе бит bDelay (что говорит за то, что она выполняет задержку);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; задача запоминает текущее значение программного счетчика;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; задача возвращает управление планировщику;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; когда у планировщика доходит очередь до проверки этой задачи, он предварительно проверяет бит bDelay  в ее дескрипторе;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; если этот бит установлен, то понятно, что задача еще не готова, и планировщик ее пропускает.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

&lt;em&gt;(Примечание. Бит bDelay сбрасывается обработчиком сервиса &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Timer).&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
Т.е. при выполнении задачей задержки &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Delay &lt;strong&gt;планировщик не передает управление задаче до конца задержки&lt;/strong&gt;. Т.е. не теряет 50 тактов на заход внутрь задачи и проверку условия.
&lt;/p&gt;

&lt;p&gt;
Таким образом, если есть некритичные к реакции события, можно ускорить работу планировщика, делая замены подобные этой:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;co1&quot;&gt;// Обычное ожидание события&lt;/span&gt;
    OS_Bsem_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;co1&quot;&gt;// Ожидание, которое ускорит работу планировщика&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        OS_Delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;              &lt;span class=&quot;co1&quot;&gt;// Проверяем условие с интервалом в 10 системных тиков&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_Check_Bsem&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;

&lt;em&gt;&lt;strong&gt;Примечание.&lt;/strong&gt; Эффективной будет только задержка с использованием таймеров задач. Статический и динамические таймеры здесь не подойдут.&lt;/em&gt;(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;)

&lt;/p&gt;
&lt;hr /&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;проверка_условия_перед_ожиданием&quot; id=&quot;проверка_условия_перед_ожиданием&quot;&gt;Проверка условия перед ожиданием&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Вернемся к описанию работы планировщика при ожидании события в какой-либо задаче, описанному в предыдущем параграфе. Как видно, вне зависимости от того, произошло событие на момент проверки или нет, мы по-любому передаем управление планировщику. Объяснение такому поведению уже приводилось: среди задач может оказаться более приоритетная, ожидающая этого же события, и управление должно быть сперва передано ей. Но допустим, что мы на 100% уверены, что второй задачи, ожидающей того же события нет. А нам жалко терять время на выход из задачи и на ожидание того, когда она станет самой приоритетной. Поэтому в таком случае можно прибегнуть к следующему приему:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;OS_Bsem_Check&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; OS_Bsem_Wait&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Т.е. мы сперва проверяем, не установлен ли уже семафор, и, только убедившись, что еще нет, возвращаемся в планировщик.
&lt;/p&gt;

&lt;p&gt;
(&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/rtos_usage#советы_по_оптимизации&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:rtos_usage&quot;&gt;К списку советов по оптимизации&lt;/a&gt;)
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Оптимизация скорости&quot; [25082-36543] --&gt;
&lt;h2&gt;&lt;a name=&quot;заключение&quot; id=&quot;заключение&quot;&gt;Заключение&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Итак, в статье были рассмотрены рекомендации по принятию решения, использовать ли ОСРВ в своих проектах или нет, рассмотрены основные рекомендации и советы по написанию более эффективных программ с использованием ОСРВ OSA, приведены ответы на часто задаваемые вопросы с объяснениями и примерами, а также произведен некий &amp;quot;взгляд изнутри&amp;quot; операционной системы OSA для более глубокого понимания функционирования планировщика и сервисов. 
&lt;/p&gt;

&lt;p&gt;
Возможно, в этой статье я не коснулся каких-то важных моментов, которые нужно было рассмотреть, или Вы, читая статью, не нашли ответ на свой вопрос, или из-за местами непонятного изложения появился какой-то новый вопрос. Может, есть какие-то замечания или предложения, - не стесняйтесь, пишите на мейл osa@pic24.ru или testerplus@mail.ru.
&lt;/p&gt;

&lt;p&gt;
Все личные имена, встречающиеся в статье, приведены с разрешения личностей, эти имена носящих. 
&lt;br/&gt;
 
&lt;br/&gt;
 
&lt;br/&gt;
 
На сайте &lt;a href=&quot;http://www.pic24.ru&quot; class=&quot;urlextern&quot; title=&quot;http://www.pic24.ru&quot;  rel=&quot;nofollow&quot;&gt;www.pic24.ru&lt;/a&gt; статья размещена с разрешения &lt;span class=&quot;important&quot;&gt;Alex'а B.&lt;/span&gt;
&lt;br/&gt;
 
&lt;br/&gt;
 
&lt;/p&gt;

&lt;p&gt;
Виктор Тимофеев, февраль 2009&lt;br/&gt;
 
&lt;a href=&quot;mailto:&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot; class=&quot;mail JSnocheck&quot; title=&quot;&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot;&gt;osa@pic24.ru&lt;/a&gt;
&lt;/p&gt;
&lt;div class=&quot;plugin_uparrow&quot;&gt;
  &lt;a href=&quot;#&quot; title=&quot;Наверх&quot;&gt;
    &lt;img src=&quot;http://www.pic24.ru/lib/plugins/uparrow/images/blue_arrow.png&quot; alt=&quot;Наверх&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Заключение&quot; [36544-] --&gt;</description>
    </item>
    <item rdf:about="http://www.pic24.ru/doku.php/osa/articles/scl?rev=1275025460">
        <dc:format>text/html</dc:format>
        <dc:date>2010-05-28T09:44:20+03:00</dc:date>
        <title>Язык SCL</title>
        <link>http://www.pic24.ru/doku.php/osa/articles/scl?rev=1275025460</link>
        <description>


&lt;h1&gt;&lt;a name=&quot;язык_scl&quot; id=&quot;язык_scl&quot;&gt;Язык SCL&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;
Виктор Тимофеев, ноябрь 2009&lt;br/&gt;
 
&lt;a href=&quot;mailto:&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot; class=&quot;mail JSnocheck&quot; title=&quot;&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot;&gt;osa@pic24.ru&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Язык SCL&quot; [1-99] --&gt;
&lt;h1&gt;&lt;a name=&quot;введение&quot; id=&quot;введение&quot;&gt;Введение&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;
Интегрированная среда MPLAB IDE имеет в своем составе встроенный симулятор, который позволяет отлаживать программу еще до программирования микросхемы. Для приближения к реальным условиям MPLAB предоставляет возможность имитировать входные сигналы с помощью встроенного инструмента Stimulus. С помощью него можно имитировать уровни на портах ввода вывода и значения регистров, причем можно привязывать эти события ко времени, к состояниям или изменениям состояний портов и регистров. Для этого интегрированной средой MPLAB предоставляется диалоговое окно (доступно через меню &amp;quot;Debugger/Stimulus&amp;quot;), в котором пользователю предлагается задать внешние воздействия. 
&lt;/p&gt;

&lt;p&gt;
Однако, когда нужно задать сигнал сложной формы, или довольно много внешних воздействий, связанных между собой во времени и состояниях, вводить имитируемые сигналы через диалоговое окно становится трудно и неудобно. Даже простая задача, например, имитация сигнала в виде манчестерского кода вызывает большие трудности из-за большего объема данных, которые требуется ввести (особенно трудности вызывает подсчет времен для асинхронной стимуляции).
&lt;/p&gt;

&lt;p&gt;
В таких случаях для задания внешних воздействий удобно пользоваться специальным встроенным языком SCL. SCL - это структурный VHDL-подобный язык высокого уровня (примечание: VHDL используется для описания аппаратуры), правда, по сравнению с ним гораздо менее функциональный. SCL представляет нам возможность организовывать циклы, формировать задержки, задавать уровни цифровых входов, имитировать аналоговые сигналы, работать с файлами.
&lt;/p&gt;

&lt;p&gt;
Язык SCL довольно плохо описан (официального описания нет), поэтому все, что здесь приведено, - результат исследования и экспериментов, а также обмена опытом с другими исследователями-экспериментаторами. Данный материал не является точным описанием языка, а всего лишь содержит общие принципы написания программ на нем. Составляя его, я решал, как лучше изложить материал: чтобы он был наиболее полным, или чтобы он был простым пособием. Остановился на втором варианте, хотя, в результате получился, скорее, первый. 
&lt;/p&gt;

&lt;p&gt;
Возможно, при описании я что-то не учел, что-то описал неполно, а где-то допустил ошибку. Не стесняйтесь ругаться, если обнаружите неточности. Если у кого-то будет какое-то сущетвенное дополнение, я обязательно внесу его в это пособие (некоторые моменты, такие как определение пользовательских типов, я умышленно не стал здесь упоминать из-за их, на мой взгляд, ненадобности и малозначимости в SCL-программах).
&lt;/p&gt;

&lt;p&gt;
Помимо самой этой статьи, я бы рекомендовал ознакомиться с двумя ветками на форуме microchip.com (на английском языке):
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://www.microchip.com/forums/tm.aspx?m=111255&quot; class=&quot;urlextern&quot; title=&quot;http://www.microchip.com/forums/tm.aspx?m=111255&quot;  rel=&quot;nofollow&quot;&gt;SCL PRIMER / TUTORIAL &lt;/a&gt; - краткое описание языка SCL;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://www.microchip.com/forums/tm.aspx?m=109149&quot; class=&quot;urlextern&quot; title=&quot;http://www.microchip.com/forums/tm.aspx?m=109149&quot;  rel=&quot;nofollow&quot;&gt;SCL Code repository&lt;/a&gt; - несколько примеров скриптов на языке SCL.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
В том же форуме по разным темам разбросаны еще несколько примеров, но я в своей статье постарался объединить все затрагиваемые там вопросы.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Введение&quot; [100-5367] --&gt;
&lt;h1&gt;&lt;a name=&quot;описание_языка&quot; id=&quot;описание_языка&quot;&gt;Описание языка&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Описание языка&quot; [5368-5411] --&gt;
&lt;h2&gt;&lt;a name=&quot;идентификаторы&quot; id=&quot;идентификаторы&quot;&gt;Идентификаторы&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Идентификаторы должны начинаться с латинской буквы и могут содержать латинские буквы, цифры и знак подчеркивания. Пример правильных идентификаторов:
&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;MyVariable
My_Variable
My_Variable_1&lt;/pre&gt;
&lt;p&gt;
Имена идентификаторов чувствительны к регистру. Имена идентификаторов не могут совпадать с зарезервированными словами.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Идентификаторы&quot; [5412-6022] --&gt;
&lt;h2&gt;&lt;a name=&quot;структура_программы&quot; id=&quot;структура_программы&quot;&gt;Структура программы&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Основное тело программы заключено в операторные скобки testbench:
&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;configuration&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;pic16f877a&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;configuration&lt;/span&gt;;
&amp;nbsp;
testbench &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;pic16f877a&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;-- Тело программы&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; testbench;&lt;/pre&gt;
&lt;p&gt;
(Для блока configuration я нашел только одно применение, о нем будет сказано в разделе &lt;a href=&quot;#стимуляция_регистров&quot; title=&quot;osa:articles:scl &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Функции: Стимуляция регистров&lt;/a&gt;. Во всех остальных случаях его можно и не ставить.)
&lt;/p&gt;

&lt;p&gt;
Само тело программы состоит из процессов, которые выполняются параллельно, но могут быть синхронизированы друг с другом:

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;Label&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;sensitivity_list&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;Определения&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;Тело процесса&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;Label&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;

Здесь:
&lt;/p&gt;
&lt;table class = &quot;fpl&quot;&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;Label&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
необязательное поле - идентификатор процесса		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;process … is&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
ключевое слово, обозначающее блок описания процесса		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;sensitivity_list&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
необязательное поле - список сигналов, по изменению которых процесс запускается.		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;begin … end process;&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
операторные скобки, внутри которых заключается тело процесса		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;Определения&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
необязательная секция, где определяются пользовательские типы, переменные и константы		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;Тело процесса&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
это уже последовательность действий, описанная операторами языка. После ключевой фразы &amp;quot;process is&amp;quot; идет необязательная секция описания типов, констант и переменных. Для программы на языке SCL типы, константы и переменные являются глобальными для всех процессов, описанных ниже (для процессов описанных выше они невидимы)		&lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;
&lt;em&gt;Примеры:&lt;/em&gt;
&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co1&quot;&gt;-- При каждом изменении состояния портов RA0 или RB1 переменная i будет увеличиваться на 1&lt;/span&gt;
TEST&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;RA0,RB1&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; i&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt;    &lt;span class=&quot;kw2&quot;&gt;integer&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    i &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; i + &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; TEST;&lt;/pre&gt;&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co1&quot;&gt;-- генерация меандра 1 KHz&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    RB0 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;not&lt;/span&gt; RB0;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;500&lt;/span&gt; us;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Тело процесса выполняется по кругу. Если его нужно выполнить только один раз, то в конце можно поставить оператор &lt;span class=&quot;important&quot;&gt;wait&lt;/span&gt; без параметров.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Структура программы&quot; [6023-9116] --&gt;
&lt;h2&gt;&lt;a name=&quot;комментарии&quot; id=&quot;комментарии&quot;&gt;Комментарии&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Комментарии в языке SCL могут начинаться с &amp;quot;--&amp;quot;, как в VHDL, или с &amp;quot;//&amp;quot;, как в Си. Все, что следует за символами &amp;quot;--&amp;quot; или &amp;quot;//&amp;quot; считается комментарием и игнорируется при трансляции.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Комментарии&quot; [9117-9480] --&gt;
&lt;h2&gt;&lt;a name=&quot;типы&quot; id=&quot;типы&quot;&gt;Типы&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Язык SCL является строго типизированным, т.е. в нем не допускается смешение типов в одной операции (например, нельзя присваивать переменной типа integer переменную типа bit; или нельзя суммировать переменную типа byte с переменной типа time).
&lt;/p&gt;

&lt;p&gt;
На данный момент язык SCL поддерживает следующие типы данных:
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Типы&quot; [9481-10046] --&gt;
&lt;h3&gt;&lt;a name=&quot;перечислимые&quot; id=&quot;перечислимые&quot;&gt;Перечислимые&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;table class = &quot;fpl&quot;&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;integer&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
32-битное целое знаковое		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;byte&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
= integer		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;word&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
= integer		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;boolean&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
Логический тип, может принимать значения &lt;span class=&quot;important&quot;&gt;true&lt;/span&gt; или &lt;span class=&quot;important&quot;&gt;false&lt;/span&gt;		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;bit&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
Битовый тип, может принимать значения &amp;#039;0&amp;#039; или &amp;#039;1&amp;#039;		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;character&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
Символьный. 		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;paddress&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
Адрес в памяти программы. 24-битовое беззнаковое целое. 		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;daddress&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
Адрес в памяти данных. 24-битовое беззнаковое целое. 		&lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Перечислимые&quot; [10047-10710] --&gt;
&lt;h3&gt;&lt;a name=&quot;временные&quot; id=&quot;временные&quot;&gt;Временные&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;table class = &quot;fpl&quot;&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;time&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
Временной. Знаковое 64-битное целое, задается в пикосекундах. Однако можно указать другие единицы измерения: ns, us, ms, sec, min, hr.		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;cycle&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
Схожий с временным типом, только время задается в циклах. Задается в виде числа с указанием единиц измерения ic (instruction cycle).		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;frequency&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
Частотный. Задается в герцах (от 0 до 1e9). Может задаваться с явным указанием единиц измерения: hz, khz, mhz.		&lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Временные&quot; [10711-11448] --&gt;
&lt;h3&gt;&lt;a name=&quot;строковые&quot; id=&quot;строковые&quot;&gt;Строковые&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;table class = &quot;fpl&quot;&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;string&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
Текстовая строка. Представляет собой массив character&amp;#039;ов (&lt;em&gt;type string is array (integer range &amp;lt;&amp;gt;) of character;&lt;/em&gt;). Применяется для вывода информации по ходу симуляции		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;line&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
Строка для обработки данных. Может быть прочитана из файла и обработана.		&lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Строковые&quot; [11449-11926] --&gt;
&lt;h3&gt;&lt;a name=&quot;файловые&quot; id=&quot;файловые&quot;&gt;Файловые&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;table class = &quot;fpl&quot;&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;text&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
Файловый. Применяется для чтения файлов (запись через этот тип произвести не удается).		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;file_open_kind&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
Тип открываемого файла. Может принимать значения: read_mode, write_mode, append_mode. (Удалось заставить работать только в режиме read_mode)		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;file_open_status&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
Состояние открытого файла. Может принимать значения: open_ok, status_error, name_error, mode_error		&lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Файловые&quot; [11927-12569] --&gt;
&lt;h3&gt;&lt;a name=&quot;остальные&quot; id=&quot;остальные&quot;&gt;Остальные&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;table class = &quot;fpl&quot;&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;severity_level&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
тип сообщения в операторе report. Может принимать значения: note, warning, error, failure.		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;array&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
позволяет определять массивы. Но в настоящее время толку от них мало, т.к. нет возможности обращаться к элементу массива по индексу		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;bit_vector&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
массив битов. Также нет возможности обращаться к элементам по индексу		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;span class=&quot;important&quot;&gt;record&lt;/span&gt;		&lt;/td&gt;
		&lt;td&gt;
Структура. Позволяет обращаться к битовым переменным SFR (например, ADCON.ADON).		&lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Остальные&quot; [12570-13342] --&gt;
&lt;h2&gt;&lt;a name=&quot;константы&quot; id=&quot;константы&quot;&gt;Константы&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Константы&quot; [13343-13380] --&gt;
&lt;h3&gt;&lt;a name=&quot;способы_задания_констант&quot; id=&quot;способы_задания_констант&quot;&gt;Способы задания констант&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

&lt;strong&gt;Целые числа&lt;/strong&gt; можно задавать с указанием системы счисления или без нее (по умолчанию число считается записанным в десятичной системе):
&lt;/p&gt;

&lt;p&gt;
&lt;span class=&quot;important&quot;&gt;система_счисления#число# &lt;/span&gt;
Причем поле &amp;quot;число&amp;quot; может содержать не только цифры (и буквы A,B,C,D,E,F), но и символ подчеркивания &amp;#039;_&amp;#039;, что очень удобно для более наглядного представления длинных чисел. Например, число 168 можно записать так:

&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;168
10#168#
16#A8#
2#10101000#
2#1010_1000#&lt;/pre&gt;

&lt;p&gt;

&lt;strong&gt;Времена&lt;/strong&gt; можно задавать в виде целого или вещественного числа с указанием единиц измерения:
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  100 ic    -- в тактах
10534 ps    -- в пикосекундах
  120 ns    -- в наносекундах
 83.3 us    -- в микросекундах
 0.11 ms    -- в миллисекундах
    5 sec   -- в секундах
  0.1 min   -- в минутах
    3 hr    -- в часах&lt;/pre&gt;
&lt;p&gt;
причем, как уже упоминалось, времена могут обозначаться как положительными, так и отрицательными числами.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Способы задания констант&quot; [13381-14865] --&gt;
&lt;h3&gt;&lt;a name=&quot;определение_констант_в_программе&quot; id=&quot;определение_констант_в_программе&quot;&gt;Определение констант в программе&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Константы определяются внутри процесса перед операторными скобками &lt;span class=&quot;important&quot;&gt;begin … end&lt;/span&gt; с ключевым словом &lt;span class=&quot;important&quot;&gt;constant&lt;/span&gt; и указанием типа и значения:

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;constant&lt;/span&gt; Start &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;time&lt;/span&gt;    &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt; us;
    &lt;span class=&quot;kw1&quot;&gt;constant&lt;/span&gt; Stop  &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;time&lt;/span&gt;    &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;300&lt;/span&gt; us;
    &lt;span class=&quot;kw1&quot;&gt;constant&lt;/span&gt; Max   &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;integer&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;16&lt;/span&gt;#7FFFFFFF#;
    &lt;span class=&quot;kw1&quot;&gt;constant&lt;/span&gt; Hello &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;string&lt;/span&gt;  &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;Hello world!&amp;quot;&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;--...&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Определение констант в программе&quot; [14866-15424] --&gt;
&lt;h2&gt;&lt;a name=&quot;переменные&quot; id=&quot;переменные&quot;&gt;Переменные&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Переменные описываются с ключевым словом &lt;span class=&quot;important&quot;&gt;variable&lt;/span&gt;, после которого следует идентификатор переменной и через двоеточие - тип переменной. Так же при определении переменной может быть сразу задано ее значение:
&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; I&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt;    &lt;span class=&quot;kw2&quot;&gt;integer&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; B&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt;    byte    &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; S&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt;    &lt;span class=&quot;kw2&quot;&gt;string&lt;/span&gt;  &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;Hello world&amp;quot;&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;--...&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Определение файловых переменных производится с ключевым словом &lt;span class=&quot;important&quot;&gt;file&lt;/span&gt;:
&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;file&lt;/span&gt;     DataFile   &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;kw2&quot;&gt;text&lt;/span&gt;;             &lt;span class=&quot;co1&quot;&gt;-- переменная, которая будет связана с&lt;/span&gt;
                                             &lt;span class=&quot;co1&quot;&gt;-- конкретным файлом&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; FileStatus &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt;  file_open_status; &lt;span class=&quot;co1&quot;&gt;-- переменная, куда будет записан результат&lt;/span&gt;
                                             &lt;span class=&quot;co1&quot;&gt;-- функции открытия файла&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;--...&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Переменные&quot; [15425-16614] --&gt;
&lt;h2&gt;&lt;a name=&quot;операторы&quot; id=&quot;операторы&quot;&gt;Операторы&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Операторы&quot; [16615-16652] --&gt;
&lt;h3&gt;&lt;a name=&quot;операции&quot; id=&quot;операции&quot;&gt;Операции&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

&lt;strong&gt;Математические операции&lt;/strong&gt;: +, -, *, /&lt;br/&gt;
 
&lt;strong&gt;Логические операции&lt;/strong&gt;: not
&lt;/p&gt;

&lt;p&gt;
(остальные операции: ^, &amp;lt;&amp;lt;, &amp;gt;&amp;gt;, %, and, or и т.д. - не поддерживаются)
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Операции&quot; [16653-16911] --&gt;
&lt;h3&gt;&lt;a name=&quot;операторы_сравнения&quot; id=&quot;операторы_сравнения&quot;&gt;Операторы сравнения&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

&amp;gt;, &amp;lt;, &amp;gt;=, &amp;lt;=, ==, != (или /=)
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Операторы сравнения&quot; [16912-17005] --&gt;
&lt;h3&gt;&lt;a name=&quot;оператор_присваивания&quot; id=&quot;оператор_присваивания&quot;&gt;Оператор присваивания&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;a &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt;;
s &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;This is a string&amp;quot;&lt;/span&gt;;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Оператор присваивания&quot; [17006-17119] --&gt;
&lt;h3&gt;&lt;a name=&quot;оператор_назначения&quot; id=&quot;оператор_назначения&quot;&gt;Оператор назначения&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Оператор назначения &amp;quot;&amp;lt;=&amp;quot; служит для задания уровней сигналов на портах ввода/вывода:

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;RB0 &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;';
RC0 &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; '0';&lt;/pre&gt;
&lt;p&gt;

Для языка SCL (в отличие от VHDL) этот оператор не отличается от оператора присваивания (разве что только его применение допустимо только к сигнальным линиям).
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Оператор назначения&quot; [17120-17661] --&gt;
&lt;h3&gt;&lt;a name=&quot;условный_оператор_if&quot; id=&quot;условный_оператор_if&quot;&gt;Условный оператор if&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;&amp;lt;&lt;/span&gt;expression&lt;span class=&quot;sy0&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
    ...
&lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;&amp;lt;&lt;/span&gt;expression2&lt;span class=&quot;sy0&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
    ...
&lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;&amp;lt;&lt;/span&gt;expression3&lt;span class=&quot;sy0&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
    ...
&lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;
    ...
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Здесь &lt;span class=&quot;important&quot;&gt;expression&lt;/span&gt; - простое выражение, результат которого может быть &amp;#039;true&amp;#039; или &amp;#039;false&amp;#039;. Например:
&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; i &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
    RB0 &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; '0';
&lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;
    RB0 &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;';
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; i + &lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
    ...
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; s &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;Hello world&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
    ...
&lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; s &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;Wellcome&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
    ...
&lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;
    ...
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Условный оператор if&quot; [17662-18226] --&gt;
&lt;h3&gt;&lt;a name=&quot;оператор_ожидания_wait&quot; id=&quot;оператор_ожидания_wait&quot;&gt;Оператор ожидания wait&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Есть четыре применения этого оператора:
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;wait_-_вечное_ожидание&quot; id=&quot;wait_-_вечное_ожидание&quot;&gt;1. wait - вечное ожидание&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;wait_for_-_ожидание_заданного_времени&quot; id=&quot;wait_for_-_ожидание_заданного_времени&quot;&gt;2. wait for - ожидание заданного времени&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;&amp;lt;&lt;/span&gt;time&amp;gt;;&lt;/pre&gt;
&lt;p&gt;
Время может быть указано как в виде константы, так и в виде переменной.
&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt; ic;
&amp;nbsp;
    MyTime &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;150&lt;/span&gt; us;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; MyTime;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;wait_until_-_ожидание_события&quot; id=&quot;wait_until_-_ожидание_события&quot;&gt;3. wait until - ожидание события&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;until&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;&amp;lt;&lt;/span&gt;event&lt;span class=&quot;sy0&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;&amp;lt;&lt;/span&gt;time&lt;span class=&quot;sy0&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;

выполняет ожидание события, заданного выражением &lt;span class=&quot;important&quot;&gt;event&lt;/span&gt;, с выходом по таймауту, указанному в &lt;span class=&quot;important&quot;&gt;time&lt;/span&gt; (необязательное поле).
&lt;/p&gt;

&lt;p&gt;
Примеры:

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;until&lt;/span&gt; RB0 !&lt;span class=&quot;sy0&quot;&gt;=&lt;/span&gt; RB1;           &lt;span class=&quot;co1&quot;&gt;-- ожидание неравенства RB0 и RB1&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;until&lt;/span&gt; RB0 &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;' &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; ms;  &lt;span class=&quot;co1&quot;&gt;-- в течение 1 мс ждем положительного фронта на RB0&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Обращу внимание на то, что если на момент начала выполнения &lt;span class=&quot;important&quot;&gt;wait until&lt;/span&gt; условие выполняется, то это не считается событием. Например, &amp;quot;wait until RB0 == &amp;#039;1&amp;#039;&amp;quot; будет ожидать не единичное состояние, а переход &amp;#039;0&amp;#039;→&amp;#039;1&amp;#039;. Т.е. если на момент выполнения wait у нас RB0 уже в состоянии &amp;#039;1&amp;#039;, то это не считается уже случившимся событием и оператор wait будет ждать сначала перехода &amp;#039;1&amp;#039;→&amp;#039;0&amp;#039;, а потом &amp;#039;0&amp;#039;→&amp;#039;1&amp;#039;.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;wait_on_-_ожидание_изменения_сигнала&quot; id=&quot;wait_on_-_ожидание_изменения_сигнала&quot;&gt;4. wait on - ожидание изменения сигнала&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;&amp;lt;&lt;/span&gt;signals_list&lt;span class=&quot;sy0&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;&amp;lt;&lt;/span&gt;time&lt;span class=&quot;sy0&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Параметры этого оператора указываются так же, как и в описании списка для &lt;span class=&quot;important&quot;&gt;process&lt;/span&gt;. Оператор переводит процесс в ожидание, пока не изменится состояние одного из сигналов, указанных в списке:

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;on&lt;/span&gt; RB0, RB1, RB2;         &lt;span class=&quot;co1&quot;&gt;-- Ждем изменения состояния на одном из трех выводов&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;on&lt;/span&gt; RA0, RA1 &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt; ms;    &lt;span class=&quot;co1&quot;&gt;-- В течение 10мс ждем изменения состояния RA0 или RA1&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Оператор ожидания wait&quot; [18227-20744] --&gt;
&lt;h3&gt;&lt;a name=&quot;оператор_цикла_loop&quot; id=&quot;оператор_цикла_loop&quot;&gt;Оператор цикла loop&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Безусловный цикл:

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;
    ...
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Цикл будет выполняться бесконечно. Прервать его выполнение можно оператором &lt;span class=&quot;important&quot;&gt;exit&lt;/span&gt; (безусловное прерывание) или &lt;span class=&quot;important&quot;&gt;exit when …&lt;/span&gt; (условное):
&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co1&quot;&gt;-- Генератор на RB0, пока на RB1 высокий уровень&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;when&lt;/span&gt; RB1 &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; '0'; &lt;span class=&quot;co1&quot;&gt;-- Прерываем цикл&lt;/span&gt;
    RB0 &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;not&lt;/span&gt; RB0;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; us;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;

Т.е. &lt;span class=&quot;important&quot;&gt;exit&lt;/span&gt; является аналогом &lt;span class=&quot;important&quot;&gt;break&lt;/span&gt; в Си. Аналога &lt;span class=&quot;important&quot;&gt;continue&lt;/span&gt; в SCL, к сожалению, нет (вернее, он есть - &lt;span class=&quot;important&quot;&gt;next&lt;/span&gt;, - но он не поддерживается)
&lt;/p&gt;

&lt;p&gt;
Внутри тела цикла должен быть хотя бы один оператор &lt;span class=&quot;important&quot;&gt;wait&lt;/span&gt;.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Оператор цикла loop&quot; [20745-21657] --&gt;
&lt;h3&gt;&lt;a name=&quot;оператор_цикла_while&quot; id=&quot;оператор_цикла_while&quot;&gt;Оператор цикла while&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;&amp;lt;&lt;/span&gt;expression&lt;span class=&quot;sy0&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;
    ...
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Цикл похож на предыдущий, но с предустановленным условием выполнения. Например, цикл из предыдущего примера выглядел бы так:
&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co1&quot;&gt;-- Генератор на RB0, пока на RB1 высокий уровень&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; RB1 &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;' &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;
    RB0 &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;not&lt;/span&gt; RB0;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; us;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Оператор цикла while&quot; [21658-22174] --&gt;
&lt;h3&gt;&lt;a name=&quot;вывод_информации_report&quot; id=&quot;вывод_информации_report&quot;&gt;Вывод информации report&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Этот оператор выводит отладочную информацию в MPLAB IDE в окно &amp;quot;Output: MPLAB_SIM&amp;quot;. Может использоваться совместно с оператором &lt;span class=&quot;important&quot;&gt;severity&lt;/span&gt;, который обозначает тип сообщения. Вывод информации служит для визуализации текущего состояния работы SCL-файла. В качестве параметра может быть как строковая константа, так и строковая переменная. Например:

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; s &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;string&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;This is string-constant&amp;quot;&lt;/span&gt;;
    s &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;This is string-variable&amp;quot;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; s;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Выведет в окно:
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;(0)  SIM-N0001 Note: This is string-constant
(0)  SIM-N0001 Note: This is string-variable&lt;/pre&gt;

&lt;p&gt;

Здесь SIM-N0001 - номер процесса, Note - тип сообщения. По умолчанию все сообщения имеют тип Note. Однако, с помощью оператора &lt;span class=&quot;important&quot;&gt;severity&lt;/span&gt; можно изменять тип сообщений на: note, warning, error, failure. Причем, note, warning и error позволяют скрипту SCL выполняться дальше, в то время как failure прерывают выполнение скрипта. Код:

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;This is a note&amp;quot;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;This is a warning&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;severity&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;warning&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;This is an error&amp;quot;&lt;/span&gt;  &lt;span class=&quot;kw1&quot;&gt;severity&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;error&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;This is a failure&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;severity&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;failure&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;This text will not be reported&amp;quot;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;

 
выведет следующую последовательность сообщений:
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;(0)  SIM-N0001 Note: This is a note
(0)  SIM-W0001 Warning: This is a warning
(0)  SIM-E0001 Error: This is an error
(0)  SIM-F0001 Failure: This is a failure&lt;/pre&gt;

&lt;p&gt;

Обратим внимание, что последняя строка уже не выводится, т.к. &lt;span class=&quot;important&quot;&gt;severity failure&lt;/span&gt; прервало работу скрипта.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Вывод информации report&quot; [22175-24382] --&gt;
&lt;h2&gt;&lt;a name=&quot;функции&quot; id=&quot;функции&quot;&gt;Функции&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;
На данный момент мне известны следующие функции:

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#стимуляция_регистров&quot; title=&quot;osa:articles:scl &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Стимуляция регистров&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;accessin&lt;/strong&gt; - назначить файл для асинхронной записи в регистр&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;triggerin&lt;/strong&gt; - назначить файл для синхронной записи в регистр&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;packetin&lt;/strong&gt; - отправить данные &lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#история_регистра&quot; title=&quot;osa:articles:scl &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;История регистра&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;accessout&lt;/strong&gt; - назначить файл для асинхронного сохранения значений регистра&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;triggerout&lt;/strong&gt; - назначить файл для синхронного сохранения значений регистра&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;triggerout_gpr&lt;/strong&gt; - назначить файл для синхронного сохранения значений произвольного регистра&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#чтение_текстовых_файлов&quot; title=&quot;osa:articles:scl &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Чтение текстовых файлов&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;file_open&lt;/strong&gt; - открыть файл&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;file_close&lt;/strong&gt; - закрыть файл&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;endfile&lt;/strong&gt; - проверка конца файла&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;readline&lt;/strong&gt; - читать строку из файла&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#обработка_строк&quot; title=&quot;osa:articles:scl &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Обработка строк&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;match&lt;/strong&gt; - проверить совпадение строк&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;read&lt;/strong&gt; - читать параметр из строки&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#время&quot; title=&quot;osa:articles:scl &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Время&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;now&lt;/strong&gt; - получение текущего времени&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;random_time&lt;/strong&gt; - получить случайное время&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;#для_отладки&quot; title=&quot;osa:articles:scl &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Для отладки&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;print&lt;/strong&gt; - вывод информации в отчет&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Функции&quot; [24383-26010] --&gt;
&lt;h3&gt;&lt;a name=&quot;стимуляция_регистров&quot; id=&quot;стимуляция_регистров&quot;&gt;Стимуляция регистров&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;br/&gt;
 
&lt;/div&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;table class=&quot;inline&quot;&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt;   &lt;span class=&quot;important&quot;&gt;accessin&lt;/span&gt; (FileName: &lt;strong&gt;string&lt;/strong&gt;, FileMode: &lt;strong&gt;mode&lt;/strong&gt;, Reg: &lt;strong&gt;register&lt;/strong&gt;, Wrap: &lt;strong&gt;boolean&lt;/strong&gt;)   &lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Назначить файл для асинхронной записи в регистр. Регистр при каждом к нему обращении (на чтение) будет заполняться данными из указанного файла.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;strong&gt;Параметры:&lt;/strong&gt;
&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;FileName&lt;/code&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Имя файла&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;FileMode&lt;/code&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Тип файла: hex_mode, dec_mode, binary_mode, formatted_mode (см. ниже)&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;Reg&lt;/code&gt;     &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Стимулируемый регистр&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;Wrap&lt;/code&gt;    &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;?&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;&lt;strong&gt;Типы файла:&lt;/strong&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;code&gt;hex_mode&lt;/code&gt; - Данные записаны в шестнадцатеричном &lt;acronym title=&quot;American Standard Code for Information Interchange&quot;&gt;ASCII&lt;/acronym&gt; представлении (на каждой строке по одномму числу)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;code&gt;dec_mode&lt;/code&gt; - Данные записаны в десятичном &lt;acronym title=&quot;American Standard Code for Information Interchange&quot;&gt;ASCII&lt;/acronym&gt; представлении (на каждой строке по одномму числу)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;code&gt;binary_mode&lt;/code&gt; - Двоичный файл&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;code&gt;formatted_mode&lt;/code&gt; - Форматированный файл &lt;em&gt;(как он форматирован я пока не разобрался)&lt;/em&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    accessin&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;adc_data.txt&amp;quot;&lt;/span&gt;, binary_mode, ADRESH, true&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;

&lt;br/&gt;
 
&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;

&lt;br/&gt;
 
&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;table class=&quot;inline&quot;&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt;   &lt;span class=&quot;important&quot;&gt;triggerin&lt;/span&gt; (FileName: &lt;strong&gt;string&lt;/strong&gt;, FileMode: &lt;strong&gt;mode&lt;/strong&gt;, Reg: &lt;strong&gt;register&lt;/strong&gt;, PC: &lt;strong&gt;paddress&lt;/strong&gt;, Wrap: &lt;strong&gt;boolean&lt;/strong&gt;)   &lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Назначить файл для синхронной записи в регистр. Регистр будет заполняться данными из указанного файла при указанном значении программного счетчика.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;strong&gt;Параметры:&lt;/strong&gt;
&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;FileName&lt;/code&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Имя файла&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;FileMode&lt;/code&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Тип файла: hex_mode, dec_mode, binary_mode, formatted_mode (см. выше)&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;Reg&lt;/code&gt;     &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Стимулируемый регистр&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;PC&lt;/code&gt;      &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Значение программного счетчика, при котором производить стимуляцию регистра (может быть указан в виде имени функции)&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;Wrap&lt;/code&gt;    &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;?&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Если значение PC указывается в виде имени функции, то это имя должно быт ьобъявлено в секции &lt;span class=&quot;important&quot;&gt;configuration&lt;/span&gt; с ключевыми словами &lt;span class=&quot;important&quot;&gt;shared label&lt;/span&gt; (нечто вроде &lt;span class=&quot;important&quot;&gt;extern&lt;/span&gt; в Си).
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Пример:&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;configuration&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;pic16f877a&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;shared&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;label&lt;/span&gt; usart_getch;      &lt;span class=&quot;co1&quot;&gt;-- Объявляем скрипту имя функции&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;configuration&lt;/span&gt;;
&amp;nbsp;
testbench &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;pic16f877a&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;-- формируем значение PORTB при каждом входе в прерывание&lt;/span&gt;
&amp;nbsp;
        triggerin&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;portb.txt&amp;quot;&lt;/span&gt;, hex_mode, PORTB, &lt;span class=&quot;nu0&quot;&gt;16&lt;/span&gt;#0004#, true&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;-- формируем значение приемного регистра USART при каждом&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;-- входе в функцию usart_getch()&lt;/span&gt;
&amp;nbsp;
        triggerin&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;portb.txt&amp;quot;&lt;/span&gt;, hex_mode, RCREG, usart_getch, true&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; testbench;&lt;/pre&gt;
&lt;p&gt;

&lt;br/&gt;
 
&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;

&lt;br/&gt;
 
&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;table class=&quot;inline&quot;&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt;   &lt;span class=&quot;important&quot;&gt;packetin&lt;/span&gt; (Data: &lt;strong&gt;line&lt;/strong&gt;, Reg: &lt;strong&gt;register&lt;/strong&gt;, Wrap: &lt;strong&gt;boolean&lt;/strong&gt;)   &lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Стимуляция значения регистра вручную. Регистр будет принимать значения, указанные в строке. Эта функция может быть применена тогда, когда регистр нужно стимулировать по более сложному алгоритму, чем допускают функции &lt;span class=&quot;important&quot;&gt;accessin&lt;/span&gt; и &lt;span class=&quot;important&quot;&gt;triggerin&lt;/span&gt;.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;strong&gt;Параметры:&lt;/strong&gt;
&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;Data&lt;/code&gt;    &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Пакет данных (список значений, разделяемых пробелами), которые нужно занести в регистр&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;Reg&lt;/code&gt;     &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Имя регистра&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;Wrap&lt;/code&gt;    &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&amp;#039;true&amp;#039; - добавить в очередь к уже имеющимся в пакете данным; &amp;#039;false&amp;#039; - затереть старые данные новым пакетом&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;
&lt;strong&gt;Пример&lt;/strong&gt; стимулирования принятых данных по UART:

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; DataLine &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;1 2 3 4 5 6 7 8 9 10&amp;quot;&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt; ms;
    packetin&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DataLine, RCREG, false&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;   &lt;span class=&quot;co1&quot;&gt;-- Стимулируем принимаемые данные&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;until&lt;/span&gt; RCREG_packet_done;       &lt;span class=&quot;co1&quot;&gt;-- Ожидание завершения приема&lt;/span&gt;
    ...
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;

&lt;br/&gt;
 
&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;

&lt;br/&gt;
 
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Стимуляция регистров&quot; [26011-30743] --&gt;
&lt;h3&gt;&lt;a name=&quot;история_регистра&quot; id=&quot;история_регистра&quot;&gt;История регистра&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;br/&gt;
 

&lt;/div&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;table class=&quot;inline&quot;&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt;   &lt;span class=&quot;important&quot;&gt;accessout&lt;/span&gt; (FileName: &lt;strong&gt;string&lt;/strong&gt;, FileMode: &lt;strong&gt;mode&lt;/strong&gt;, Reg: &lt;strong&gt;register&lt;/strong&gt;, Wrap)   &lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Назначить файл для асинхронного сохранения значений регистра. Значение регистра при каждом к нему обращении (на запись) будет сохраняться в указанный файл.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;strong&gt;Параметры:&lt;/strong&gt;
&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;FileName&lt;/code&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Имя файла&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;FileMode&lt;/code&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Тип файла: hex_mode, dec_mode, binary_mode, formatted_mode (см. выше)&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;Reg&lt;/code&gt;     &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Наблюдаемый регистр&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;Wrap&lt;/code&gt;    &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;?&lt;/dd&gt;
&lt;/dl&gt;

&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    accessout&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;eeprom_data.txt&amp;quot;&lt;/span&gt;, hex_mode, EEDATA&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;

&lt;br/&gt;
 
&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;

&lt;br/&gt;
 
&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;table class=&quot;inline&quot;&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt;   &lt;span class=&quot;important&quot;&gt;triggerout&lt;/span&gt; (FileName: &lt;strong&gt;string&lt;/strong&gt;, FileMode: &lt;strong&gt;mode&lt;/strong&gt;, Reg: &lt;strong&gt;register&lt;/strong&gt;, PC: &lt;strong&gt;paddress&lt;/strong&gt;)   &lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Назначить файл для синхронного сохранения значений регистра. Значение регистра будет записываться в указанный файл при каждой установке программного счетчика в указанное значение.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;strong&gt;Параметры:&lt;/strong&gt;
&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;FileName&lt;/code&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Имя файла&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;FileMode&lt;/code&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Тип файла: hex_mode, dec_mode, binary_mode, formatted_mode (см. выше)&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;Reg&lt;/code&gt;     &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Наблюдаемый регистр&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;PC&lt;/code&gt;      &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Значение программного счетчика, при котором производить запись значения регистра в файл (может быть указан в виде имени функции)&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Если значение PC указывается в виде имени функции, то это имя должно быт ьобъявлено в секции &lt;span class=&quot;important&quot;&gt;configuration&lt;/span&gt; с ключевыми словами &lt;span class=&quot;important&quot;&gt;shared label&lt;/span&gt;.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Пример:&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;configuration&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;pic16f877a&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;shared&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; eewrite;      &lt;span class=&quot;co1&quot;&gt;-- Объявляем скрипту имя функции&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;configuration&lt;/span&gt;;
&amp;nbsp;
testbench &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;pic16f877a&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;-- Сохраняем все значения регистра PORTA на момент входа в прерывание&lt;/span&gt;
&amp;nbsp;
        triggerout&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;porta.txt&amp;quot;&lt;/span&gt;, hex_mode, PORTA, &lt;span class=&quot;nu0&quot;&gt;16&lt;/span&gt;#0004#&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;-- Сохраняем все записываемые в EEPROM данные&lt;/span&gt;
&amp;nbsp;
        triggerout&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;eeprom.txt&amp;quot;&lt;/span&gt;, hex_mode, EEDATA, eewrite&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; testench;&lt;/pre&gt;
&lt;p&gt;

&lt;br/&gt;
 
&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;

&lt;br/&gt;
 
&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;table class=&quot;inline&quot;&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt;   &lt;span class=&quot;important&quot;&gt;triggerout_gpr&lt;/span&gt; (FileName: &lt;strong&gt;string&lt;/strong&gt;, FileMode: &lt;strong&gt;mode&lt;/strong&gt;, GPR: ???, PC: &lt;strong&gt;paddress&lt;/strong&gt;, Size: &lt;strong&gt;integer&lt;/strong&gt;)   &lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Назначить файл для синхронного сохранения значений регистра. Значение регистра будет записываться в указанный файл при каждой установке программного счетчика в указанное значение.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;strong&gt;Параметры:&lt;/strong&gt;
&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;FileName&lt;/code&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Имя файла&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;FileMode&lt;/code&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Тип файла: hex_mode, dec_mode, binary_mode, formatted_mode (см. выше)&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;GPR&lt;/code&gt;     &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Имя переменной&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;PC&lt;/code&gt;      &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Значение программного счетчика, при котором производить запись значения регистра в файл (может быть указан в виде имени функции)&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;Size&lt;/code&gt;    &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Количество байт на одну запись (в текстовом режиме будут разделены пробелами)&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Для того, чтобы скрипт знал имя используемой переменной, ему нужно его сообщеть в секции &lt;span class=&quot;important&quot;&gt;configuration&lt;/span&gt; c ключевыми словами &lt;span class=&quot;important&quot;&gt;shared variable&lt;/span&gt;:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Пример:&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;configuration&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;pic16f877a&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;shared&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; MyCounter;           &lt;span class=&quot;co1&quot;&gt;-- Объявляем скрипту имя переменной&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;configuration&lt;/span&gt;;
&amp;nbsp;
testbench &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;pic16f877a&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;-- Сохраняем все значения переменной MyCounter при входе в прерывание&lt;/span&gt;
&amp;nbsp;
        triggerout&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;counter.txt&amp;quot;&lt;/span&gt;,dec_mode, MyCounter, &lt;span class=&quot;nu0&quot;&gt;16&lt;/span&gt;#0004#&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; testbench;&lt;/pre&gt;
&lt;p&gt;

&lt;br/&gt;
 
&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;

&lt;br/&gt;
 
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;История регистра&quot; [30744-35294] --&gt;
&lt;h3&gt;&lt;a name=&quot;чтение_текстовых_файлов&quot; id=&quot;чтение_текстовых_файлов&quot;&gt;Чтение текстовых файлов&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;br/&gt;
 

&lt;/div&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;table class=&quot;inline&quot;&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt;   &lt;span class=&quot;important&quot;&gt;file_open&lt;/span&gt; (Status: &lt;strong&gt;file_open_status&lt;/strong&gt;, F: &lt;strong&gt;text&lt;/strong&gt;, Name: &lt;strong&gt;string&lt;/strong&gt;, Kind: &lt;strong&gt;file_open_kind&lt;/strong&gt;   &lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Открывает указанный файл и связывает его с файловой переменной.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;strong&gt;Параметры&lt;/strong&gt;:&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;Status&lt;/code&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;в эту переменную будет записан результат открытия файла (open_ok, status_error, name_error, mode_error)&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;F&lt;/code&gt;     &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;файловая переменная&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;Name&lt;/code&gt;  &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;имя файла в текстовом виде&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;Kind&lt;/code&gt;  &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;режим работы файла (read_mode,write_mode,append_mode). Хоть SCL и позволяет открывать файлы в режимах write и append, и даже создает файлы с указанным именем на диске, но произвести запись в эти файлы так и не удалось. Так что на сегодняшний день реально из файлов можно только читать.&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;&lt;strong&gt;Пример:&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;file&lt;/span&gt;     MyFile    &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;text&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; Result    &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; file_open_result;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    file_open&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Result, MyFile, &lt;span class=&quot;st0&quot;&gt;&amp;quot;data.txt&amp;quot;&lt;/span&gt;, read_mode&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; Result !&lt;span class=&quot;sy0&quot;&gt;=&lt;/span&gt; open_ok &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;Error opening file data.txt!&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;severity&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;failure&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
&lt;br/&gt;

&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;

&lt;br/&gt;
 
&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;table class=&quot;inline&quot;&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt;   &lt;span class=&quot;important&quot;&gt;file_close&lt;/span&gt; (F: &lt;strong&gt;text&lt;/strong&gt;)   &lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Закрывает открытый файл&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;strong&gt;Параметр:&lt;/strong&gt;&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;F&lt;/code&gt;  &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;файловая переменная&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;&lt;strong&gt;Пример:&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;file&lt;/span&gt;     MyFile    &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;text&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; Result    &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; file_open_result;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    file_open&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Result, MyFile, &lt;span class=&quot;st0&quot;&gt;&amp;quot;data.txt&amp;quot;&lt;/span&gt;, read_mode&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    ...
    file_close&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;MyFile&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
&lt;br/&gt;

&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;

&lt;br/&gt;
 
&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;table class=&quot;inline&quot;&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt;   &lt;span class=&quot;important&quot;&gt;endfile&lt;/span&gt; (F: &lt;strong&gt;text&lt;/strong&gt;)   &lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Проверка конца файла. Возвращает &amp;#039;true&amp;#039;, если конец файла, иначе - &amp;#039;false&amp;#039;.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;strong&gt;Параметр:&lt;/strong&gt;&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;F&lt;/code&gt;  &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;файловая переменная&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;&lt;strong&gt;Пример:&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;file&lt;/span&gt;     MyFile    &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;text&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; Result    &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; file_open_result;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    file_open&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Result, MyFile, &lt;span class=&quot;st0&quot;&gt;&amp;quot;data.txt&amp;quot;&lt;/span&gt;, read_mode&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; endfile&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;MyFile&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; false &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;
        ...
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
&lt;br/&gt;

&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;

&lt;br/&gt;
 
&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;table class=&quot;inline&quot;&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt;   &lt;span class=&quot;important&quot;&gt;readline&lt;/span&gt; (F: &lt;strong&gt;text&lt;/strong&gt;, Data: &lt;strong&gt;line&lt;/strong&gt;)   &lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Читает строку из файла.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;strong&gt;Параметр:&lt;/strong&gt;&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;F&lt;/code&gt;   &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;файловая переменная&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;Data&lt;/code&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;переменная, куда будет помещена прочитанная строка&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;&lt;strong&gt;Пример:&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;file&lt;/span&gt;     MyFile    &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;text&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; Result    &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; file_open_result;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; Data      &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;line&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    file_open&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Result, MyFile, &lt;span class=&quot;st0&quot;&gt;&amp;quot;data.txt&amp;quot;&lt;/span&gt;, read_mode&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; endfile&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;MyFile&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; false &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;
        readline&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;MyFile, Data&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        ...
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Чтение текстовых файлов&quot; [35295-38326] --&gt;
&lt;h3&gt;&lt;a name=&quot;обработка_строк&quot; id=&quot;обработка_строк&quot;&gt;Обработка строк&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;p&gt;

&lt;br/&gt;
 

&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;table class=&quot;inline&quot;&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt;   &lt;span class=&quot;important&quot;&gt;match&lt;/span&gt; (Line: &lt;strong&gt;line&lt;/strong&gt;, Pattern: &lt;strong&gt;string&lt;/strong&gt;)   &lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Проверить совпадение строк. Вернее, совпадение начала первой строки со второй строкой. Функция возвращает &amp;#039;true&amp;#039;, если начало первой строки совпадает со второй строкой, и &amp;#039;false&amp;#039;, - если не совпадает.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;strong&gt;Параметры:&lt;/strong&gt;&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;Line&lt;/code&gt;   &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;здесь содержится строка, начало которой мы проверяем&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;Pattern&lt;/code&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;здесь находится шаблон для сравнения&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;
&lt;strong&gt;Пример:&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; MyLine    &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;This is a line&amp;quot;&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;MyLine, &lt;span class=&quot;st0&quot;&gt;&amp;quot;This&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; true &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;       &lt;span class=&quot;co1&quot;&gt;-- условие выполнится&lt;/span&gt;
        ...
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;MyLine, &lt;span class=&quot;st0&quot;&gt;&amp;quot;This is &amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; true &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;   &lt;span class=&quot;co1&quot;&gt;-- условие выполнится&lt;/span&gt;
        ...
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;MyLine, &lt;span class=&quot;st0&quot;&gt;&amp;quot;is&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; true &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;         &lt;span class=&quot;co1&quot;&gt;-- условие не выполнится&lt;/span&gt;
        ...
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;MyLine, &lt;span class=&quot;st0&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; true &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;           &lt;span class=&quot;co1&quot;&gt;-- Проверка на пустую строку&lt;/span&gt;
        ...
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
&lt;br/&gt;

&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;

&lt;br/&gt;
 

&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;table class=&quot;inline&quot;&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt;   &lt;span class=&quot;important&quot;&gt;read&lt;/span&gt; (Line: &lt;strong&gt;line&lt;/strong&gt;, Data: any type)   &lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; читает слово из строки и заносит результат в переменную Data. Само слово затем из строки удаляется. Длина слова зависит от типа Data: &lt;/div&gt;
&lt;ul&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; string - будет прочитана вся строка&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; time или cycle - будут прочитаны два слова, разделенные пробелами&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; integer, byte, word - будет прочитано одно слово&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;strong&gt;Параметры:&lt;/strong&gt;&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;Line&lt;/code&gt;   &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Исходная строка&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;Data&lt;/code&gt;   &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Переменная, куда будет помещен результат&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;&lt;strong&gt;Пример:&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;Line&lt;/span&gt;  &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;wait 100 ms&amp;quot;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; Delay &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;time&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; Dummy &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;integer&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw2&quot;&gt;Line&lt;/span&gt;, &lt;span class=&quot;st0&quot;&gt;&amp;quot;wait&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; true &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
        read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw2&quot;&gt;Line&lt;/span&gt;, Dummy&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                &lt;span class=&quot;co1&quot;&gt;-- Убираем слово &amp;quot;wait&amp;quot;, теперь Line = &amp;quot;100 ms&amp;quot;&lt;/span&gt;
        read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw2&quot;&gt;Line&lt;/span&gt;, Delay&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                &lt;span class=&quot;co1&quot;&gt;-- Delay = 100 ms, Line = &amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; Delay;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
    ...
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Обращу внимание на чтение в переменную Dummy. Когда мы попадаем внутрь блока &lt;span class=&quot;important&quot;&gt;if&lt;/span&gt;, у нас строка до сих пор содержит &amp;quot;wait 100 ms&amp;quot;. Нам нужно убрать одно слово из нее, для чего мы выполняем функцию &lt;span class=&quot;important&quot;&gt;read&lt;/span&gt;, занося результат в любую переменную типа &lt;span class=&quot;important&quot;&gt;integer&lt;/span&gt; (т.е. читаем до первого пробела). После этого прочитанное слово удаляется из исходной строки. Далее выполняем &lt;span class=&quot;important&quot;&gt;read&lt;/span&gt; еще раз с параметром &lt;span class=&quot;important&quot;&gt;Delay&lt;/span&gt;, который имеет тип &lt;span class=&quot;important&quot;&gt;time&lt;/span&gt;, т.е. читаем два слова: &amp;quot;100 ms&amp;quot;. После этого строка &lt;span class=&quot;important&quot;&gt;Line&lt;/span&gt; становится пустой.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Обработка строк&quot; [38327-41688] --&gt;
&lt;h3&gt;&lt;a name=&quot;время&quot; id=&quot;время&quot;&gt;Время&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;p&gt;

&lt;br/&gt;
 

&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;table class=&quot;inline&quot;&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt;   &lt;span class=&quot;important&quot;&gt;now&lt;/span&gt; ()   &lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Получение текущего времени. Возвращает текущее время симуляции. В зависимости от того, какого типа переменную мы используем для хранения результата (&lt;span class=&quot;important&quot;&gt;cycle&lt;/span&gt; или &lt;span class=&quot;important&quot;&gt;time&lt;/span&gt;), будут выбраны соответствующие единицы измерения.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;strong&gt;Пример:&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;Time&lt;/span&gt;  &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;time&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; Cycle &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; cycle;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt; ms;
    &lt;span class=&quot;kw2&quot;&gt;Time&lt;/span&gt;  &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; now&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;      &lt;span class=&quot;co1&quot;&gt;-- получение времени в пикосекундах&lt;/span&gt;
    Cycle &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; now&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;      &lt;span class=&quot;co1&quot;&gt;-- получение времени в тактах контроллера&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
&lt;br/&gt;

&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;

&lt;br/&gt;
 

&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;table class=&quot;inline&quot;&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt;&lt;span class=&quot;important&quot;&gt;random_time&lt;/span&gt; (LowerLimit: &lt;strong&gt;integer&lt;/strong&gt;, UpperLimit: &lt;strong&gt;integer&lt;/strong&gt;, Units: &lt;strong&gt;string&lt;/strong&gt;, Seed1: &lt;strong&gt;integer&lt;/strong&gt;, Seed2: &lt;strong&gt;integer&lt;/strong&gt;, Result: &lt;strong&gt;time&lt;/strong&gt;)&lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Формирует случайное значение для переменной типа &lt;span class=&quot;important&quot;&gt;time&lt;/span&gt;.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;strong&gt;Параметры:&lt;/strong&gt;&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;LowerLimit&lt;/code&gt;  &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Нижний предел&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;UpperLimit&lt;/code&gt;  &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Верхний предел&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;Units&lt;/code&gt;       &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Единицы измерения (&amp;quot;&amp;quot;ps&amp;quot;, &amp;quot;ns&amp;quot;, &amp;quot;us&amp;quot;, &amp;quot;ms&amp;quot;, &amp;quot;sec&amp;quot;, &amp;quot;min&amp;quot;, &amp;quot;hr&amp;quot;)&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;Seed1, Seed2&lt;/code&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;параметры генератора случайных чисел (в самом простом случае можно задать любыми ненулевыми значениями)&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;code&gt;Result&lt;/code&gt;      &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Результат работы функции&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;&lt;strong&gt;Пример&lt;/strong&gt; (генератор шума):
&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;testbench &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;pic16f877a&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; RandTime  &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;time&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; RandSeed1, RandSeed2&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;integer&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
        RandSeed1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1234&lt;/span&gt;;
        RandSeed2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;5678&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;
            random_time&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;, &lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt;, &lt;span class=&quot;st0&quot;&gt;&amp;quot;us&amp;quot;&lt;/span&gt;, RandSeed1, RandSeed2, RandTime&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
            &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; RandTime;
            RB0 &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;not&lt;/span&gt; RB0;
        &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; testbench;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Время&quot; [41689-43759] --&gt;
&lt;h3&gt;&lt;a name=&quot;для_отладки&quot; id=&quot;для_отладки&quot;&gt;Для отладки&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;
&lt;hr /&gt;

&lt;p&gt;

&lt;br/&gt;
 

&lt;/p&gt;

&lt;/div&gt;
&lt;div class=&quot;level4&quot;&gt;
&lt;table class=&quot;inline&quot;&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt;   &lt;span class=&quot;important&quot;&gt;print&lt;/span&gt; (…)   &lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Эта функция похожа на оператор &lt;span class=&quot;important&quot;&gt;report&lt;/span&gt;, но в отличие от него она, помимо строк, может выводить значения переменных (целочисленных, битовых, булевых, временных, строковых), а также выводить по несколько значений на одной строке.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;strong&gt;Параметры&lt;/strong&gt;: 
эта функция может принимать не более 5 параметров различных типов: line, string, integer, character, boolean, bit, time, cycle.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Пример:&lt;/strong&gt;

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;    I &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;25&lt;/span&gt;;
    print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Current I = &amp;quot;&lt;/span&gt;, I&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Выведет:
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;(0)  SIM-N0001 Note: Current I = 25&lt;/pre&gt;

&lt;p&gt;

&lt;br/&gt;

&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;

&lt;br/&gt;
 
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Для отладки&quot; [43760-44633] --&gt;
&lt;h2&gt;&lt;a name=&quot;работа_с_файлами&quot; id=&quot;работа_с_файлами&quot;&gt;Работа с файлами&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;
Мы уже упоминали функции для стимуляции регистров значениями из файлов (assignin, triggerin). Эти функции при своей простоте имеют недостатки:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; неудобно стимулировать несколько регистров синхронно (актуально, например, для пары ADRESH:ADRESL);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; довольно бедный выбор условий обновления значений регистра (либо при обращении к нему, либо по значению PC);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; нет возможности менять правила стимуляции в ходе работы;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; нет возможности стимулировать отдельные биты регистра.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Избавиться от этих недостатков можно, читая данные из файлов напрямую, используя специальные   &lt;a href=&quot;#чтение_текстовых_файлов&quot; title=&quot;osa:articles:scl &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;функции работы с файлами&lt;/a&gt; и функции &lt;a href=&quot;#обработка_строк&quot; title=&quot;osa:articles:scl &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;обработки строк&lt;/a&gt;. На сегодняшний день есть возможность работать только с текстовыми файлами. Это с одной стороны немного ограничивает нас в способе предоставления информации (нельзя обрабатывать бинарные), но с другой - позволяет снабдить файл сопроводительной информацией (например, паузы, комментарии). 
&lt;/p&gt;

&lt;p&gt;
Рассмотрим порядок обработки файла data.txt следующего содержания (задача взята наобум):

&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;porta 0
portb 0
wait 10 ms
rcreg 1 2 3 4 5 6 7
porta 16 portb 255
wait 10 ms
porta 0 portb 0&lt;/pre&gt;

&lt;p&gt;

Сначала мы обнуляем значения регистров PORTA и PORTB. Через 10 мс отправляем данные по USART, после чего устанавливаем порты в значения PORTA = 0x10, PORTB = 0xFF. А еще через 10 мс порты снова обнуляются.
&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;testbench &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;pic16f877a&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;file&lt;/span&gt;     InFile  &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;text&lt;/span&gt;;               &lt;span class=&quot;co1&quot;&gt;-- Файловая переменная для связи с файлом&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; Status  &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; file_open_status;   &lt;span class=&quot;co1&quot;&gt;-- Для проверки правильности открытия файла&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; InLine  &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;line&lt;/span&gt;;               &lt;span class=&quot;co1&quot;&gt;-- Для обработки прочитанных данных&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; Delay   &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;time&lt;/span&gt;;               &lt;span class=&quot;co1&quot;&gt;-- Для формирования задержек&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;Work&lt;/span&gt;    &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;integer&lt;/span&gt;;            &lt;span class=&quot;co1&quot;&gt;-- Рабочая переменная для обработки данных&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
    file_open&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Status, InFile, &lt;span class=&quot;st0&quot;&gt;&amp;quot;data.txt&amp;quot;&lt;/span&gt;, read_mode&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; Status !&lt;span class=&quot;sy0&quot;&gt;=&lt;/span&gt; open_ok &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;                                    &lt;span class=&quot;co1&quot;&gt;-- Обработка ошибки&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;Ошибка открытия файла data.txt!&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;severity&lt;/span&gt; failed;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;co1&quot;&gt;-- Работаем с файлом, пока в нем есть данные&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; endfile&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InFile&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; false &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;
&amp;nbsp;
        readline&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InFile, InLine&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                &lt;span class=&quot;co1&quot;&gt;-- Читаем строку&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;co1&quot;&gt;-- Обрабатываем строку, пока в ней есть данные&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, &lt;span class=&quot;st0&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; false &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;
&amp;nbsp;
            &lt;span class=&quot;co1&quot;&gt;-- Обработка команды&lt;/span&gt;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;     match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, &lt;span class=&quot;st0&quot;&gt;&amp;quot;porta&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; true &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
&amp;nbsp;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, &lt;span class=&quot;kw2&quot;&gt;Work&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;              &lt;span class=&quot;co1&quot;&gt;-- Убираем из InLine слово &amp;quot;porta&amp;quot;&lt;/span&gt;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, &lt;span class=&quot;kw2&quot;&gt;Work&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;              &lt;span class=&quot;co1&quot;&gt;-- Читаем параметр&lt;/span&gt;
                PORTA &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;Work&lt;/span&gt;;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, &lt;span class=&quot;st0&quot;&gt;&amp;quot;portb&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; true &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
&amp;nbsp;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, &lt;span class=&quot;kw2&quot;&gt;Work&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;              &lt;span class=&quot;co1&quot;&gt;-- Убираем из InLine слово &amp;quot;portb&amp;quot;&lt;/span&gt;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, &lt;span class=&quot;kw2&quot;&gt;Work&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;              &lt;span class=&quot;co1&quot;&gt;-- Читаем параметр&lt;/span&gt;
                PORTB &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;Work&lt;/span&gt;;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, &lt;span class=&quot;st0&quot;&gt;&amp;quot;rcreg&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; true &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
&amp;nbsp;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, &lt;span class=&quot;kw2&quot;&gt;Work&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;              &lt;span class=&quot;co1&quot;&gt;-- Убираем из InLine слово &amp;quot;rcreg&amp;quot;&lt;/span&gt;
                packetin&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, RCREG, true&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;   &lt;span class=&quot;co1&quot;&gt;-- Перенаправляем остаток строки в приемный буфер&lt;/span&gt;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, &lt;span class=&quot;st0&quot;&gt;&amp;quot;wait&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;  &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; true &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
&amp;nbsp;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, &lt;span class=&quot;kw2&quot;&gt;Work&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;              &lt;span class=&quot;co1&quot;&gt;-- Убираем из InLine слово &amp;quot;wait&amp;quot;&lt;/span&gt;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, Delay&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;             &lt;span class=&quot;co1&quot;&gt;-- Читаем параметр задержки&lt;/span&gt;
                &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; Delay;                  &lt;span class=&quot;co1&quot;&gt;-- Выдерживаем задержку&lt;/span&gt;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;
                &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;Неизвестная команда&amp;quot;&lt;/span&gt;;
            &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt;;  &lt;span class=&quot;co1&quot;&gt;-- line == &amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt;;  &lt;span class=&quot;co1&quot;&gt;-- endfile&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;Готово!&amp;quot;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; testbench;&lt;/pre&gt;
&lt;p&gt;
В этом примере следует сосредоточить внимание на двух циклах: &lt;span class=&quot;important&quot;&gt;while endfile(InFile) == false&lt;/span&gt; и &lt;span class=&quot;important&quot;&gt;while match(InLine, &amp;quot;&amp;quot;) == false&lt;/span&gt;. Эти два цикла - основа работы с файлами и присутствуют почти в любой программе на языке SCL, которая занимается обработкой входного файла. Внутри этих вложенных один в другой циклов и происходит вся обработка данных. Т.е. если файл еще содержит данные (проверяем функцией endfile), то читаем строку, а затем по одной достаем из строки команды (вспоминаем, что функция read из строки удаляет прочитанное слово) и их параметры, пока строка не закончится. 
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Отметим одно важное свойство&lt;/strong&gt;: с одним и тем же файлом параллельно могут работать несколько процессов. 
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Работа с файлами&quot; [44634-50902] --&gt;
&lt;h1&gt;&lt;a name=&quot;программирование&quot; id=&quot;программирование&quot;&gt;Программирование&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;
В этом параграфе рассмотрим некоторые особенности и сложности работы с SCL файлами при работе с интегрированной средой MPLAB.

&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Программирование&quot; [50903-51180] --&gt;
&lt;h2&gt;&lt;a name=&quot;подключение_программы_scl_к_симулятору&quot; id=&quot;подключение_программы_scl_к_симулятору&quot;&gt;Подключение программы SCL к симулятору&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;
После того, как программа генерации входных сигналов написана, нам нужно ее подключить. Для этого нужно (действия для версии MPLAB 8.xx):
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; выбрать в качестве отладчика MPLAB SIM (меню &amp;quot;Debugger/Select Tool&amp;quot;);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; создать рабочую книгу через меню &amp;quot;Debug/Stimulus/New Workbook&amp;quot; (или Open Workbook, если книга уже была создана);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; в открывшемся диалоговом окне &amp;quot;Stimulus&amp;quot; нажать кнопку &amp;quot;Advanced…&amp;quot; в нижнем левом углу; откроется маленькое диалоговое окно &amp;quot;Advanced operations&amp;quot;;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; в этом новом окне нажать кнопку &amp;quot;Attach&amp;quot; (прикрепить SCL-файл); выбрать файл с программой SCL.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Если программа была написана правильно, то в поле &amp;quot;Override Workbook with Stimulus File&amp;quot; появится имя выбранного файла. Если нет, значит, в программе есть ошибки и их нужно исправлять. Как - описано в следующем параграфе.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Подключение программы SCL к симулятору&quot; [51181-52555] --&gt;
&lt;h2&gt;&lt;a name=&quot;сложности_отладки&quot; id=&quot;сложности_отладки&quot;&gt;Сложности отладки&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;
Отладка SCL-файлов - дело довольно трудоемкое, поскольку сама среда MPLAB предоставляет довольно скудный инструментарий для отладки. Начать можно с того, что встроенный парсер SCL-файлов выдает очень лаконичные сообщения по поводу ошибок, и часто долго не понимаешь, что ему может не нравиться.
&lt;/p&gt;

&lt;p&gt;
Работая с SCL-файлами, желательно, чтобы на виду было рабочее окно &amp;quot;Output&amp;quot; с открытой вкладкой &amp;quot;MPLAB SIM&amp;quot;. Все сообщения об ошибках, а также вся отладочная информация, формируемая самим SCL-скриптом (print и report) будут выводиться в это окно. Чем нам MPLAB IDE помогает при отладке? Во-первых, он обеспечивает подсветку синтаксиса для файлов SCL, что позволяет избежать некоторых малозаметных помарок. Во-вторых, при прикреплении SCL-файла в окно &amp;quot;Output&amp;quot; выводится информация с номером строки, содержащей ошибку, и тип ошибки (здесь он довольно скуп на сообщения и чаще всего пишет &amp;quot;syntax error&amp;quot;); кроме того, двойным щелчком мышки по сообщению в окне &amp;quot;Output&amp;quot; мы попадаем на строку с ошибкой в исходном файле.
&lt;/p&gt;

&lt;p&gt;
Я бы рекомендовал подключать файл SCL еще на начальном этапе программирования. Т.е., сначала создаем пустой файл:

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;testbench &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;pic16f877a&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; testbench;&lt;/pre&gt;
&lt;p&gt;

потом подключаем его по описанному выше алгоритму, убеждаемся, что он подключился (в окне Output должно появиться сообщение &amp;quot;Stimulus: SCL file attached successfully.&amp;quot;). Теперь можно писать SCL-скрипт, причем для проверки того, что все написано правильно уже не нужно делать &amp;quot;Detach/Attach&amp;quot;, а достаточно всего лишь нажать кнопку F6 (Reset при отладке). По этой кнопке файл SCL автоматически перезагружается и проходит повторную проверку. В окне Output будут появляться сообщения об ошибках, если они есть, или ничего не будет появляться, если ошибок нет. (Когда ничего не появляется, это несколько настораживает, поэтому я во все свои SCL-скрипты встраиваю такой процесс:

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;======================== SCL loaded OK! ==========================&amp;quot;&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;

Если нет ошибок, то по нажатию F6 в окне Output будет появляться надпись &amp;quot;SCL-file loaded OK.&amp;quot;)
&lt;/p&gt;

&lt;p&gt;
Надо отметить, что у нас нет возможности пройти по шагам SCL-скрипт. Поэтому даже если и нет ошибок синтаксических, которые обнаружит парсер, у нас могут быть ошибки алгоритмические. Их находить труднее, потому что мы не можем расставлять в SCL-программах точки останова и отслеживать в каком-то окне текущие значения переменных. Но здесь нам на помощь приходят &lt;span class=&quot;important&quot;&gt;report&lt;/span&gt; и &lt;span class=&quot;important&quot;&gt;print&lt;/span&gt;, использование которых может помочь нам проследить ход выполнения SCL-скрипта и значения переменных в определенных точках выполняемого скрипта.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Сложности отладки&quot; [52556-57040] --&gt;
&lt;h2&gt;&lt;a name=&quot;недостаток_операторов&quot; id=&quot;недостаток_операторов&quot;&gt;Недостаток операторов&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;
Язык SCL хоть и является подмножеством языка VHDL, но все-таки имеет далеко не все его возможности. Стоит отметить отсутствие некоторых операторов (&lt;span class=&quot;important&quot;&gt;case&lt;/span&gt;, &lt;span class=&quot;important&quot;&gt;for&lt;/span&gt;, &lt;span class=&quot;important&quot;&gt;after&lt;/span&gt;), типов (&lt;span class=&quot;important&quot;&gt;real&lt;/span&gt;, полноценных массивов), математических и логических операций (&lt;span class=&quot;important&quot;&gt;xor&lt;/span&gt;, &lt;span class=&quot;important&quot;&gt;and&lt;/span&gt;, &lt;span class=&quot;important&quot;&gt;or&lt;/span&gt;, &lt;span class=&quot;important&quot;&gt;%&lt;/span&gt;, сдвиги) и пр. Это все доставляет некоторые неудобства, но, тем не менее, большинство конструкций могут быть заменены.
&lt;/p&gt;

&lt;p&gt;
Во-первых, у нас нет возможности использовать сложные выражения в условных операторах. Мы не можем написать:

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;   &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; RA0 &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;' &lt;span class=&quot;kw1&quot;&gt;and&lt;/span&gt; RA1 &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;' &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
       ...&lt;/pre&gt;
&lt;p&gt;

Нам придется делать вложенные условия:

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;   &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; RA0 &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;' &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
       &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; RA1 &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;' &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
           ...&lt;/pre&gt;
&lt;p&gt;
Это немного усложняет структуру программы, т.к. появляется очень много операторных блоков.
&lt;/p&gt;

&lt;p&gt;
Кроме того, сильно огорчает отсутствие выделения битовых полей. Например, мы не можем воспользоваться операцией &lt;span class=&quot;important&quot;&gt;and&lt;/span&gt;:

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;    i &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; j &lt;span class=&quot;kw1&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;16&lt;/span&gt;#0E#;&lt;/pre&gt;
&lt;p&gt;

потому, что такой операции нет в языке SCL. Или, что еще обиднее, нет простого механизма сдвига числа с извлечением младшего бита. Однако это не значит, что такое невозможно. Например, наш предыдущий пример с j &amp;amp; 0x0E может быть решен с помощью математических операций *, /, -:

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;    i &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; j / &lt;span class=&quot;nu0&quot;&gt;16&lt;/span&gt;;
    i &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; j - i * &lt;span class=&quot;nu0&quot;&gt;16&lt;/span&gt;;    &lt;span class=&quot;co1&quot;&gt;-- Отрезаем все биты выше 3-го&lt;/span&gt;
    i &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; i / &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;;
    i &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; i * &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;;         &lt;span class=&quot;co1&quot;&gt;-- Отрезаем нулевой бит&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Примерно так же решается задача сдвига с извлечением младшего бита:

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;    b &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;65&lt;/span&gt;;            &lt;span class=&quot;co1&quot;&gt;-- Данные, которые надо последовательно втолкнуть на вход RB0&lt;/span&gt;
    i &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;8&lt;/span&gt;;             &lt;span class=&quot;co1&quot;&gt;-- обрабатываем 8 бит&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; i &lt;span class=&quot;sy0&quot;&gt;&amp;gt;&lt;/span&gt; 0 &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;
        i &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; i - &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
        j &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; b / &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;;
        j &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; b - j * &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;; &lt;span class=&quot;co1&quot;&gt;-- Получили в j младший бит из переменой b&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; j &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;  &lt;span class=&quot;co1&quot;&gt;-- Вталкиваем этот бит в RB0&lt;/span&gt;
            RB0 &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;';
        &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;
            RB0 &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; '0';
        &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
        b &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; b / &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;;     &lt;span class=&quot;co1&quot;&gt;-- деление на 2 - эквивалент сдвига влево&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt; us;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Недостаток операторов&quot; [57041-60106] --&gt;
&lt;h2&gt;&lt;a name=&quot;доступ_к_регистрам_контроллера&quot; id=&quot;доступ_к_регистрам_контроллера&quot;&gt;Доступ к регистрам контроллера&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;
Во время выполнения SCL-скрипта у нас есть возможность обращаться к регистрам контроллера. Мы можем получить текущее значения программно счетчика, значение регистра FSR, значения таймеров, регистров управления модулем CCP и т.д. Здесь я особо глубоко исследования не проводил. Но с сожалением должен заметить, что не все регистры можно прочитать. Например, на это:

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;    i &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; PORTA;&lt;/pre&gt;
&lt;p&gt;

парсер ругается &amp;quot;Type error in assignment&amp;quot;. В то время как значения остальных регистров прочитать таким образом удается. (Также не читаются TRISx, TXREG и еще некоторые периферийные регистры). В принципе, это не страшно, т.к., во-первых, операция чтения регистра PORTA довольно редкая, а во-вторых, ее можно заменить побитовым заполнением:

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;    i &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; 0;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; RA0 &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;' &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
        i &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; i + &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; RA1 &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;' &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
        i &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; i + &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; RA2 &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;' &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
        i &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; i + &lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; RA3 &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;' &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
        i &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; i + &lt;span class=&quot;nu0&quot;&gt;8&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; RA4 &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;' &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
        i &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; i + &lt;span class=&quot;nu0&quot;&gt;16&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; RA5 &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;' &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
        i &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; i + &lt;span class=&quot;nu0&quot;&gt;32&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;

Встроенный в MPLAB симулятор сам накладывает кое-какие ограничения. Их поиском я не занимался, а когда и натыкался на них, то старался обходить, при этом, к сожалению, не отмечая для себя на будущее. По отзывам с форума на microchip.com есть ограничения на стимуляцию регистров АЦП для dsPIC&amp;#039;ов, но я лично не проверял.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Доступ к регистрам контроллера&quot; [60107-62388] --&gt;
&lt;h1&gt;&lt;a name=&quot;примеры&quot; id=&quot;примеры&quot;&gt;Примеры&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;
Здесь приведу несколько примеров готовых скриптов. Сначала совсем простые, потом посложнее. Основная цель, которую я преследовал, выбирая задачи для скриптов, не в том, чтобы их можно было использовать для отладки ваших программ, а в том, чтобы, глядя на них, у вас сложилось более четкое представление о структуре скрипта, порядке его работы и о его возможностях.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Примеры&quot; [62389-63086] --&gt;
&lt;h2&gt;&lt;a name=&quot;генератор_1_khz&quot; id=&quot;генератор_1_khz&quot;&gt;Генератор 1 KHz&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Для начала самый простой пример - генератор.

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;testbench &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;pic16f877a&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
    RB0 &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; '0';
    &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;
        RB0 &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;not&lt;/span&gt; RB0;    &lt;span class=&quot;co1&quot;&gt;-- каждые 0.5мс меняем состояние порта&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;500&lt;/span&gt; us;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; testbench;&lt;/pre&gt;
&lt;p&gt;
В результате работы данного скрипта мы получим такой сигнал на входе RB0:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/graph_1khz.png?id=osa%3Aarticles%3Ascl&quot; class=&quot;media&quot; title=&quot;osa:articles:graph_1khz.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/graph_1khz.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Можем его немного усложнить, добавив управление из самой отлаживаемой программы:

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co1&quot;&gt;-------------------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-- Данный скрипт демонстрирует управляемый генератор 1 KHz. Генерируемый сигнал&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-- подается на вход RB0. Управляется выходом RB1 (=1 - есть генерация, =0 - нет)&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
testbench &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;pic16f877a&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
    RB0 &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; '0';
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; RB1 &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;' &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;    &lt;span class=&quot;co1&quot;&gt;-- Меняем состояние порта, только если есть сигнал&lt;/span&gt;
            RB0 &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;not&lt;/span&gt; RB0;   &lt;span class=&quot;co1&quot;&gt;-- разрешения (&amp;quot;1&amp;quot; на выходе RB1)&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;
            RB0 &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; '0';
        &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;500&lt;/span&gt; us;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; testbench;&lt;/pre&gt;
&lt;p&gt;

Теперь в программе для микроконтроллера, устанавливая порт RB1 в &amp;quot;1&amp;quot;, мы включаем генерацию на RB0 из SCL-скрипта.
Для примера была написана небольшая программа, которая формирует на выводе RB1 меандр частотой 50 Гц для управления генератором. На графике видно, что как только на RB1 появляется &amp;quot;1&amp;quot;, скрипт начинает генерировать на вход RB0 меандр частотой 1 КГц.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/graph_1khz_controlled.png?id=osa%3Aarticles%3Ascl&quot; class=&quot;media&quot; title=&quot;osa:articles:graph_1khz_controlled.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/graph_1khz_controlled.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Генератор 1 KHz&quot; [63087-65346] --&gt;
&lt;h2&gt;&lt;a name=&quot;генератор_программного_uart&quot; id=&quot;генератор_программного_uart&quot;&gt;Генератор программного UART&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;
Большинство контроллеров PIC младшего и среднего семейства имеют на борту всего один аппаратный USART-модуль, но часто бывает нужно управлять двумя и более устройствами посредством этого интерфейса. При отладке программы хорошо бы убедиться в том, что прораммный UART-приемник работает корректно. Вот пример скрипта, который генерирует на вход ПИКу сигнал UART. Скорость выбирается пользователем (по умолчанию 9600), данные берутся из файла.
&lt;/p&gt;

&lt;p&gt;
Пример исходного файла data_uart.txt:

&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;baudrate 19200
wait 1 ms
10 20 30 40 50 60
wait 3 ms
55 66 77 88&lt;/pre&gt;

&lt;p&gt;

Здесь &lt;span class=&quot;important&quot;&gt;baudrate&lt;/span&gt; - команда на изменение скорости, &lt;span class=&quot;important&quot;&gt;wait&lt;/span&gt; - команда на выдержку паузы. Числа даны в десятичной системе счисления (как их задавать в шестнадцатеричной или в символьном виде, - не знаю).
&lt;/p&gt;

&lt;p&gt;
В добавок, данный пример является некоей иллюстрацией синхронизации двух процессов через внутреннюю переменную. Скрипт разбит на два процесса: один читает файлы и формирует данные для генерации сигнала, а второй формирует сам сигнал. Процессы выполняются параллельно. В конкрентом случае можно было обойтись и без разделения программы на два процесса, т.к. у нас всего один процесс (и только в одном месте) запускает второй. Но в общем случае это может оказаться полезным, когда:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; байт может отправляется из разных мест процесса;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; байт может отправляться из разных процессов;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; во время отправки данных головной процесс должен заниматься своими делами.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Для синхронизации служит общая переменная UART_Work. Когда она = 0, процесс отправки байта свободен. Для отправки байта головной процесс должен подготовить данные для отправки (записать нужное значение в UART_Data), а потом установить UART_Work в &amp;quot;1&amp;quot;, тем самым сообщив процессу отправки, что можно начинать работать. Процесс отправки сам обнулит эту переменную, когда байт будет полностью передан, сообщем тем самым основному процессу, что передатчик вновь свободен.
&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co1&quot;&gt;-------------------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-- Данный скрипт демонстрирует формирование программного UART сигнала на входе&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-- контроллера PORTC, 5. Данные читаются из файла &amp;quot;data_uart.txt&amp;quot;&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-------------------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-- Пример файла с данными:&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-------------------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;--  baudrate 19200&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;--  wait 1 ms&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;--  10 20 30 40 50 60&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;--  wait 3 ms&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;--  55 66 77 88&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
testbench &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;pic16f877a&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;======================== SCL loaded OK! ==========================&amp;quot;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;---------------------------------------------------&lt;/span&gt;
&amp;nbsp;
uart_send_byte&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;              &lt;span class=&quot;co1&quot;&gt;-- Подпрограмма вывода одного байта по последовательному&lt;/span&gt;
                                        &lt;span class=&quot;co1&quot;&gt;-- интерфейсу UART через вход RC5&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; UART_Delay   &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;time&lt;/span&gt;;       &lt;span class=&quot;co1&quot;&gt;-- Длительность одного бита&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; UART_Data    &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; byte;       &lt;span class=&quot;co1&quot;&gt;-- Передаваемый байт&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; UART_Bit     &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; byte;       &lt;span class=&quot;co1&quot;&gt;-- Для выделения бита при передаче&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; UART_Counter &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;integer&lt;/span&gt;;    &lt;span class=&quot;co1&quot;&gt;-- Счетчик битов&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; UART_Work    &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;integer&lt;/span&gt;;    &lt;span class=&quot;co1&quot;&gt;-- Установкой этой переменной в 1 из другого&lt;/span&gt;
                                        &lt;span class=&quot;co1&quot;&gt;-- процесса мы инициируем начало передачи.&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
    RC5 &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;';
    UART_Work &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; 0;                     &lt;span class=&quot;co1&quot;&gt;-- Сообщаем головной программе, что процесс свободен,&lt;/span&gt;
                                        &lt;span class=&quot;co1&quot;&gt;-- моно передавать новый байт&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; UART_Work &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; 0 &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;           &lt;span class=&quot;co1&quot;&gt;-- Ожидаем команду на старт от основного процесса&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; ic;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;;
&amp;nbsp;
    RC5 &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; '0';                         &lt;span class=&quot;co1&quot;&gt;-- Формируем стартовый бит&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; UART_Delay;
&amp;nbsp;
    UART_Counter &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;8&lt;/span&gt;;                  &lt;span class=&quot;co1&quot;&gt;-- цикл для 8 бит данных&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; UART_Counter &lt;span class=&quot;sy0&quot;&gt;&amp;gt;&lt;/span&gt; 0 &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;
&amp;nbsp;
        UART_Counter &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; UART_Counter - &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
        UART_Bit &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; UART_Data / &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;;      &lt;span class=&quot;co1&quot;&gt;-- Выделяем младший бит&lt;/span&gt;
        UART_Bit &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; UART_Data - UART_Bit * &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;;
        UART_Data &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; UART_Data / &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; UART_Bit &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; 0 &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;           &lt;span class=&quot;co1&quot;&gt;-- Отправляем его&lt;/span&gt;
            RC5 &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; '0';
        &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;
            RC5 &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;';
        &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; UART_Delay;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;;
&amp;nbsp;
    RC5 &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;';                         &lt;span class=&quot;co1&quot;&gt;-- Формируем стоповый бит&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; UART_Delay;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; uart_send_byte;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;---------------------------------------------------&lt;/span&gt;
&amp;nbsp;
uart&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;                            &lt;span class=&quot;co1&quot;&gt;-- Читаем данные из файла и формируем&lt;/span&gt;
                                            &lt;span class=&quot;co1&quot;&gt;-- из них сигнал для контроллера&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;file&lt;/span&gt;     InFile &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;text&lt;/span&gt;;                 &lt;span class=&quot;co1&quot;&gt;-- Рабочий файл&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; Status &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; file_open_status;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; InLine &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;line&lt;/span&gt;;                 &lt;span class=&quot;co1&quot;&gt;-- Строка, прочитанная из файла&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; i      &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;integer&lt;/span&gt;;              &lt;span class=&quot;co1&quot;&gt;-- Рабочая переменная&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; Delay  &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;time&lt;/span&gt;;                 &lt;span class=&quot;co1&quot;&gt;-- Рабочая задержка&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
    UART_Delay &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;9600&lt;/span&gt;;             &lt;span class=&quot;co1&quot;&gt;-- скорость по умолчанию 9600&lt;/span&gt;
&amp;nbsp;
                                            &lt;span class=&quot;co1&quot;&gt;-- Открываем файл с данными&lt;/span&gt;
    file_open&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Status, InFile, &lt;span class=&quot;st0&quot;&gt;&amp;quot;data_uart.txt&amp;quot;&lt;/span&gt;, read_mode&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; Status !&lt;span class=&quot;sy0&quot;&gt;=&lt;/span&gt; open_ok &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;Ошибка открытия файла data_uart.txt&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;severity&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;failure&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; endfile&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InFile&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; false &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;     &lt;span class=&quot;co1&quot;&gt;-- Обрабатываем, пока файл не пуст&lt;/span&gt;
&amp;nbsp;
        readline&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InFile, InLine&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Прочитана строка: &amp;quot;&lt;/span&gt;, InLine&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, &lt;span class=&quot;st0&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; false &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;
&amp;nbsp;
            &lt;span class=&quot;co1&quot;&gt;-- Проверяем строку на наличие команды&lt;/span&gt;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, &lt;span class=&quot;st0&quot;&gt;&amp;quot;baudrate&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; true &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;   &lt;span class=&quot;co1&quot;&gt;-- Изменение скорости&lt;/span&gt;
&amp;nbsp;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;            &lt;span class=&quot;co1&quot;&gt;-- Удаляем из строки слово &amp;quot;baudrate&amp;quot;&lt;/span&gt;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;            &lt;span class=&quot;co1&quot;&gt;-- Читаем значение скорости&lt;/span&gt;
&amp;nbsp;
                &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; i !&lt;span class=&quot;sy0&quot;&gt;=&lt;/span&gt; 0 &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
                    UART_Delay &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / i;            &lt;span class=&quot;co1&quot;&gt;-- Устанавливаем новую скорость&lt;/span&gt;
                    print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Новая скорость UART = &amp;quot;&lt;/span&gt;, i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
                &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;
                    print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;ОШИБКА в файле: Скорость не может быть = 0!&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
                &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, &lt;span class=&quot;st0&quot;&gt;&amp;quot;wait&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; true &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;    &lt;span class=&quot;co1&quot;&gt;-- Задержка&lt;/span&gt;
&amp;nbsp;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;            &lt;span class=&quot;co1&quot;&gt;-- Удаляем из строки слово &amp;quot;wait&amp;quot;&lt;/span&gt;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, Delay&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;        &lt;span class=&quot;co1&quot;&gt;-- Читаем значение задержки&lt;/span&gt;
                print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Ждем &amp;quot;&lt;/span&gt;, Delay&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
                &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; Delay;             &lt;span class=&quot;co1&quot;&gt;-- формируем задержку&lt;/span&gt;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;                                        &lt;span class=&quot;co1&quot;&gt;-- Передача байта&lt;/span&gt;
&amp;nbsp;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, UART_Data&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                &lt;span class=&quot;co1&quot;&gt;-- Читаем байт&lt;/span&gt;
                print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Отправка байта &amp;quot;&lt;/span&gt;, UART_Data&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
                UART_Work &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;                         &lt;span class=&quot;co1&quot;&gt;-- Даем команду на начало передачи&lt;/span&gt;
&amp;nbsp;
                &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; UART_Work &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;               &lt;span class=&quot;co1&quot;&gt;-- Ожидаем завершения передачи&lt;/span&gt;
                    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; ic;
                &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;; &lt;span class=&quot;co1&quot;&gt;-- match InLine&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;; &lt;span class=&quot;co1&quot;&gt;-- endfile&lt;/span&gt;
&amp;nbsp;
    file_close&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InFile&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; uart;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; testbench;&lt;/pre&gt;
&lt;p&gt;
Ниже приведен график генерируемого сигнала. На нем отчетливо видно, что данные передаются двумя пакетами с интервалом 3 мс.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/graph_uart.png?id=osa%3Aarticles%3Ascl&quot; class=&quot;media&quot; title=&quot;osa:articles:graph_uart.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/graph_uart.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Генератор программного UART&quot; [65347-75154] --&gt;
&lt;h2&gt;&lt;a name=&quot;генератор_аналогового_сигнала&quot; id=&quot;генератор_аналогового_сигнала&quot;&gt;Генератор аналогового сигнала&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;
Симулятор в интегрированной среде MPLAB IDE не позволяет имитировать разные уровни напряжения на аналоговых входах. Однако он позволяет симулировать регистры ADRESH и ADRESL напрямую. Этим мы можем воспользоваться для имитации аналоговых сигналов.
&lt;/p&gt;

&lt;p&gt;
Ниже приведен пример, который будет заполнять регистры ADRESH:ADRESL значениями из файла. Это будет происходить в тот момент, когда программа будет обращаться к АЦП. Это пример является только демонстрацией необходимых действия для формирования регитсров ADRES, он не подходит для практического применения, поскольку входные данные синхронизированы только с обращениями программы к АЦП. Т.е. если обращений не будет, то и сигналы формироваться не будут (или другими словами: когда бы мы не обратились к АЦП, мы всегда прочитаем один и тот же сигнал). На практике такое не встречается.
&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;testbench &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;pic16f877a&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;======================== SCL loaded OK! ==========================&amp;quot;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;---------------------------------------------------&lt;/span&gt;
&amp;nbsp;
ADC&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;file&lt;/span&gt;     InFile &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;text&lt;/span&gt;;                     &lt;span class=&quot;co1&quot;&gt;-- Рабочий файл&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; Status &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; file_open_status;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; InLine &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;line&lt;/span&gt;;                     &lt;span class=&quot;co1&quot;&gt;-- Строка, прочитанная из файла&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; Value  &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;integer&lt;/span&gt;;                  &lt;span class=&quot;co1&quot;&gt;-- Рабочая переменная&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;constant&lt;/span&gt; FileName &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;data_adc.txt&amp;quot;&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
    file_open&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Status, InFile, FileName, read_mode&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; Status !&lt;span class=&quot;sy0&quot;&gt;=&lt;/span&gt; open_ok &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;Ошибка открытия файла!&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;severity&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;failure&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; endfile&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InFile&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; false &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;         &lt;span class=&quot;co1&quot;&gt;-- Обрабатываем, пока файл не пуст&lt;/span&gt;
&amp;nbsp;
        readline&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InFile, InLine&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Прочитана строка: &amp;quot;&lt;/span&gt;, InLine&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, &lt;span class=&quot;st0&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; false &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;   &lt;span class=&quot;co1&quot;&gt;-- Обрабатываем строку, пока она не пуста&lt;/span&gt;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;until&lt;/span&gt; ADCON0.GO_nDONE &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;';  &lt;span class=&quot;co1&quot;&gt;-- Ждем, когда программа начнет чтение ADC&lt;/span&gt;
            read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine, Value&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; ADCON0.ADON &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;' &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
&amp;nbsp;
                print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Формируем значение &amp;quot;&lt;/span&gt;, Value&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
                &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; ADCON0.GO_nDONE &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;' &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;co1&quot;&gt;-- Ждем завершения измерения (для 877a&lt;/span&gt;
                    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; ic;                &lt;span class=&quot;co1&quot;&gt;-- этот бит сбрасывается MPLAB SIM'ом&lt;/span&gt;
                &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;;                         &lt;span class=&quot;co1&quot;&gt;-- автоматически. Но, мо-моему, для неко-&lt;/span&gt;
                                                  &lt;span class=&quot;co1&quot;&gt;-- торых контроллеров эту нужно делать&lt;/span&gt;
                                                  &lt;span class=&quot;co1&quot;&gt;-- вручную)&lt;/span&gt;
&amp;nbsp;
                &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; ADCON1.ADFM &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; '0' &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;        &lt;span class=&quot;co1&quot;&gt;-- Левое выравнивание&lt;/span&gt;
&amp;nbsp;
                    ADRESH &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; Value /  &lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;;
                    ADRESL &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; Value * &lt;span class=&quot;nu0&quot;&gt;64&lt;/span&gt;;
&amp;nbsp;
                &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;
                                                  &lt;span class=&quot;co1&quot;&gt;-- Правое выравнивание&lt;/span&gt;
                    ADRESH &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; Value / &lt;span class=&quot;nu0&quot;&gt;256&lt;/span&gt;;
                    ADRESL &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; Value;
&amp;nbsp;
                &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;; &lt;span class=&quot;co1&quot;&gt;-- ADON&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;; &lt;span class=&quot;co1&quot;&gt;-- match InLine&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;; &lt;span class=&quot;co1&quot;&gt;-- endfile&lt;/span&gt;
&amp;nbsp;
    file_close&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InFile&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;Конец скрипта&amp;quot;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; ADC;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; testbench;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Генератор аналогового сигнала&quot; [75155-79267] --&gt;
&lt;h2&gt;&lt;a name=&quot;генератор_dtmf&quot; id=&quot;генератор_dtmf&quot;&gt;Генератор DTMF&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;
Итак, как уже было сказано, для практического применения предыдущий пример не очень подходит. Представьте, что нам нужно оцифровать и распознать DTMF сигнал. Для этого нам нужно точно вымерять моменты обращения к АЦП, а если скрипт синхронизирован только с самим обращением, то он будет работать при любых условиях. В новом примере мы создадим реальную модель аналогового сигнала, по которой уже можно будет отлаживать программу.
&lt;/p&gt;

&lt;p&gt;
Напомню: DTMF сигнал - это сигнал, получаемый суммированием двух синусоидальных сигналов из двух наборов частот: 

&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; 697 Hz, 770 Hz, 852 Hz, 941 Hz&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; 1209 Hz, 1336 Hz, 1477 Hz, 1633 Hz&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
Скрипт будет состоять из 4-х процессов:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;span class=&quot;important&quot;&gt;FREQ1&lt;/span&gt; - генератор синуса заданной частоты&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;span class=&quot;important&quot;&gt;FREQ2&lt;/span&gt; - генератор синуса заданной частоты&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;span class=&quot;important&quot;&gt;WORK&lt;/span&gt; - Управляющий - будет читать файл с данными и формировать целеуказания для FREQ1 и FREQ2&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;span class=&quot;important&quot;&gt;ADC&lt;/span&gt; - независимый процесс, который при обращении пользовательской программы к АЦП будет формировать значения регистров ADRESH:ADRESL по мгновенным значениям синусов, генерируемых процессами FREQ1 и FREQ2.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

(Примечание: еще добавлены пятый процесс, в который для удобства вынесены объявления глобальных переменных, и шестой, который содержит строку &amp;quot;SCL loaded OK!&amp;quot;)
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;формат_файла_data_dtmf.txt&quot; id=&quot;формат_файла_data_dtmf.txt&quot;&gt;Формат файла &amp;quot;data_dtmf.txt&amp;quot;&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;
Этот файл содержит список команд для управления процессом. В самом простом случае он может состоять из чисел от 1 до 16 (0 - будет воспринят как 16), из которых будет генерироваться последовательность тонов. Однако, с помощью доолнительных команд можно менять параметры сигналов:
&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;strong&gt;freq&lt;/strong&gt; &amp;lt;c&amp;gt; &amp;lt;f&amp;gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Генерировать синус частоты f на канале c (c = 1 или 2)&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;strong&gt;stop&lt;/strong&gt;        &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;остановить генерацию (отключает оба канала)&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;strong&gt;duration&lt;/strong&gt; &amp;lt;d&amp;gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;задать длительность тонов (по умолчанию 100 мс)&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;strong&gt;pause&lt;/strong&gt; &amp;lt;p&amp;gt;   &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;задать паузу между тонами (по умолчанию 100 мс)&lt;/dd&gt;
&lt;dt&gt;&lt;span class='term'&gt; &lt;strong&gt;level&lt;/strong&gt;       &lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;задать уровень сигнала от 0% до 100% (по умолчанию 100%)&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Также допускается использование комментариев
&lt;/p&gt;

&lt;p&gt;
Пример рабочего файла:

&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;  
// Задаем временные параметры тонов
duration 30 ms
pause 30 ms

// Генерируем все тоны по очереди
1 2 3 4 5 6 7 8 

// Уменьшаем уровень сигнала втрое 
level 33

9 10 11 12 13 14 15 16

// Генерируем опорную частоту 1 КГц на первом канале
freq 1 1000
wait 100 ms
stop

// Генерируем опорную частоту 2 КГц на втором канале
freq 2 2000
wait 100 ms
stop
&lt;/pre&gt;

&lt;p&gt;
Вот отчет его обработки:

&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;(0)  SIM-N0001 Note: ======================== SCL loaded OK! ==========================
(0)  SIM-N0001 Note: Line:  // Задаем временные параметры тонов
(0)  SIM-N0001 Note: Line:  duration 30 ms
(0)  SIM-N0001 Note: Установлено время гудка  30000000 ns
(0)  SIM-N0001 Note: Line:  pause 30 ms
(0)  SIM-N0001 Note: Установлено время паузы  30000000 ns
(0)  SIM-N0001 Note: Line:
(0)  SIM-N0001 Note: Line:  // Генерируем все тоны по очереди
(0)  SIM-N0001 Note: Line:  1 2 3 4 5 6 7 8
(0)  SIM-N0001 Note: Генерируется тон:  1
ADC-W0001:	Tad time is less than 1.60us
ADC-W0008: No stimulus file attached to ADRESL for A/D.
(60000)  SIM-N0001 Note: Генерируется тон:  2
(120000)  SIM-N0001 Note: Генерируется тон:  3
(180000)  SIM-N0001 Note: Генерируется тон:  4
(240000)  SIM-N0001 Note: Генерируется тон:  5
(300000)  SIM-N0001 Note: Генерируется тон:  6
(360000)  SIM-N0001 Note: Генерируется тон:  7
(420000)  SIM-N0001 Note: Генерируется тон:  8
(480000)  SIM-N0001 Note: Line:
(480000)  SIM-N0001 Note: Line:  // Уменьшаем уровень сигнала втрое
(480000)  SIM-N0001 Note: Line:  level 33
(480000)  SIM-N0001 Note: Установлен уровень сигнала =  33 %
(480000)  SIM-N0001 Note: Line:
(480000)  SIM-N0001 Note: Line:  9 10 11 12 13 14 15 16
(480000)  SIM-N0001 Note: Генерируется тон:  9
(540000)  SIM-N0001 Note: Генерируется тон:  10
(600000)  SIM-N0001 Note: Генерируется тон:  11
(660000)  SIM-N0001 Note: Генерируется тон:  12
(720000)  SIM-N0001 Note: Генерируется тон:  13
(780000)  SIM-N0001 Note: Генерируется тон:  14
(840000)  SIM-N0001 Note: Генерируется тон:  15
(900000)  SIM-N0001 Note: Генерируется тон:  16
(900000)  SIM-N0001 Note: Line:
(900000)  SIM-N0001 Note: Line:  // Генерируем опорную частоту 1 КГц на первом канале
(900000)  SIM-N0001 Note: Line:  freq 1 1000
(900000)  SIM-N0001 Note: Частота канала 1 =  1000
(900000)  SIM-N0001 Note: Sample rate =  976 ns
(900000)  SIM-N0001 Note: OK
(900000)  SIM-N0001 Note: Line:  wait 100 ms
(900000)  SIM-N0001 Note: Ждем  100000000 ns
(1000000)  SIM-N0001 Note: Line:  stop
(1000000)  SIM-N0001 Note: Line:
(1000000)  SIM-N0001 Note: Line:  // Генерируем опорную частоту 2 КГц на втором канале
(1000000)  SIM-N0001 Note: Line:  freq 2 2000
(1000000)  SIM-N0001 Note: Частота канала 2 =  2000
(1000000)  SIM-N0001 Note: OK
(1000000)  SIM-N0001 Note: Line:  wait 100 ms
(1000000)  SIM-N0001 Note: Ждем  100000000 ns
(1100000)  SIM-N0001 Note: Line:  stop
(1100000)  SIM-N0001 Note: Line:
(1100000)  SIM-F0001 Failure: Работа скрипта успешно завершена&lt;/pre&gt;
&lt;p&gt;
&lt;br/&gt;
 
&lt;br/&gt;
 
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;текст_скрипта&quot; id=&quot;текст_скрипта&quot;&gt;Текст скрипта&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;

Наконец, сам скрипт:
&lt;/p&gt;

&lt;p&gt;
(файл с таблицей синуса прилагается &lt;a href=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/sinus.rar&quot; class=&quot;media mediafile mf_rar&quot; title=&quot;osa:articles:sinus.rar&quot;&gt;sinus.rar&lt;/a&gt;)
&lt;/p&gt;

&lt;p&gt;
&lt;br/&gt;
 

&lt;/p&gt;
&lt;pre class=&quot;vhdl code vhdl&quot; style=&quot;font-family:monospace;&quot;&gt;testbench &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;pic16f877a&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;-------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;======================== SCL loaded OK! ==========================&amp;quot;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt;;
&amp;nbsp;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;-------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-- Определение глобальных переменных (использующиеся более чем одним&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-- процессом) вынесены в отдельный пустой процесс для наглядности&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
variables &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;constant&lt;/span&gt; DTMFFileName   &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;string&lt;/span&gt;  &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;data_dtmf.txt&amp;quot;&lt;/span&gt;;   &lt;span class=&quot;co1&quot;&gt;-- Файл с командами&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;constant&lt;/span&gt; SinusFileName  &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;string&lt;/span&gt;  &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;sinus.txt&amp;quot;&lt;/span&gt;;       &lt;span class=&quot;co1&quot;&gt;-- Файл, содержащий 1024 выборки&lt;/span&gt;
                                                            &lt;span class=&quot;co1&quot;&gt;-- одного периода синусоиды&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;constant&lt;/span&gt; NumOfSinPoints &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;integer&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1024&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; SignalLevel    &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;integer&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt;;               &lt;span class=&quot;co1&quot;&gt;-- Уровень сигнала (в процентах)&lt;/span&gt;
&amp;nbsp;
                                                            &lt;span class=&quot;co1&quot;&gt;-- ДЛЯ КАНАЛА 1:&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; SampleRate_1   &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;time&lt;/span&gt;;                         &lt;span class=&quot;co1&quot;&gt;-- Скорость выборки&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; AnalogSignal_1 &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;integer&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; 0;                 &lt;span class=&quot;co1&quot;&gt;-- Мгновенное значение сигнала&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; FreqEnable_1   &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; false;             &lt;span class=&quot;co1&quot;&gt;-- true - генерировать сигнал&lt;/span&gt;
                                                            &lt;span class=&quot;co1&quot;&gt;-- false - молчать&lt;/span&gt;
&amp;nbsp;
                                                            &lt;span class=&quot;co1&quot;&gt;-- ДЛЯ КАНАЛА 2:&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; SampleRate_2   &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;time&lt;/span&gt;;                         &lt;span class=&quot;co1&quot;&gt;-- Скорость выборки&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; AnalogSignal_2 &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;integer&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; 0;                 &lt;span class=&quot;co1&quot;&gt;-- Мгновенное значение сигнала&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; FreqEnable_2   &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; false;             &lt;span class=&quot;co1&quot;&gt;-- true - генерировать сигнал&lt;/span&gt;
                                                            &lt;span class=&quot;co1&quot;&gt;-- false - молчать&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
    SampleRate_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;697&lt;/span&gt;;
    SampleRate_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; SampleRate_1 / NumOfSinPoints;
    SampleRate_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;1209&lt;/span&gt;;
    SampleRate_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; SampleRate_2 / NumOfSinPoints;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; variables;
&amp;nbsp;
&amp;nbsp;
&amp;nbsp;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;-------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-- Основной процесс. Читает из файла data_dtmf.txt команды и&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-- выполняет их, выдавая целеуказания процессам FREQ1 и FREQ2.&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw2&quot;&gt;WORK&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;file&lt;/span&gt;     DTMF_File      &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;text&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; Status         &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; file_open_status;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; i, j           &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;integer&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; DTMF_Line      &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;line&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; DTMF_Pause     &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;time&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt; ms;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; DTMF_Duration  &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;time&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt; ms;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; WaitTime       &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;time&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
    file_open&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Status, DTMF_File, DTMFFileName, read_mode&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; Status !&lt;span class=&quot;sy0&quot;&gt;=&lt;/span&gt; open_ok &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
        print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Ошибка открытия файла &amp;quot;&lt;/span&gt;, DTMFFileName&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;Скрипт приостановлен&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;severity&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;failure&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; endfile&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_File&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; false &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;
&amp;nbsp;
        readline&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_File, DTMF_Line&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Line: &amp;quot;&lt;/span&gt;,  DTMF_Line&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, &lt;span class=&quot;st0&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; false &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;
&amp;nbsp;
            &lt;span class=&quot;co1&quot;&gt;-----------------------------------------------------&lt;/span&gt;
            &lt;span class=&quot;co1&quot;&gt;-- Пропускаем комментарии&lt;/span&gt;
            &lt;span class=&quot;co1&quot;&gt;-----------------------------------------------------&lt;/span&gt;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, &lt;span class=&quot;st0&quot;&gt;&amp;quot;//&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; true &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
&amp;nbsp;
                &lt;span class=&quot;kw1&quot;&gt;exit&lt;/span&gt;;                               &lt;span class=&quot;co1&quot;&gt;-- выходим из цикла&lt;/span&gt;
&amp;nbsp;
            &lt;span class=&quot;co1&quot;&gt;-----------------------------------------------------&lt;/span&gt;
            &lt;span class=&quot;co1&quot;&gt;-- Пауза в обработке скрипта&lt;/span&gt;
            &lt;span class=&quot;co1&quot;&gt;-----------------------------------------------------&lt;/span&gt;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, &lt;span class=&quot;st0&quot;&gt;&amp;quot;wait&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; true &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;  &lt;span class=&quot;co1&quot;&gt;--&lt;/span&gt;
&amp;nbsp;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                 &lt;span class=&quot;co1&quot;&gt;-- Удаляем команду из строки&lt;/span&gt;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, WaitTime&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
                print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Ждем &amp;quot;&lt;/span&gt;, WaitTime&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
                &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; WaitTime;
&amp;nbsp;
            &lt;span class=&quot;co1&quot;&gt;-----------------------------------------------------&lt;/span&gt;
            &lt;span class=&quot;co1&quot;&gt;-- Длительность сигнала&lt;/span&gt;
            &lt;span class=&quot;co1&quot;&gt;-----------------------------------------------------&lt;/span&gt;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, &lt;span class=&quot;st0&quot;&gt;&amp;quot;duration&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; true &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
&amp;nbsp;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                 &lt;span class=&quot;co1&quot;&gt;-- Удаляем команду из строки&lt;/span&gt;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, DTMF_Duration&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
                print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Установлено время гудка &amp;quot;&lt;/span&gt;, DTMF_Duration&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
            &lt;span class=&quot;co1&quot;&gt;-----------------------------------------------------&lt;/span&gt;
            &lt;span class=&quot;co1&quot;&gt;-- Длительность паузы&lt;/span&gt;
            &lt;span class=&quot;co1&quot;&gt;-----------------------------------------------------&lt;/span&gt;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, &lt;span class=&quot;st0&quot;&gt;&amp;quot;pause&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; true    &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
&amp;nbsp;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                 &lt;span class=&quot;co1&quot;&gt;-- Удаляем команду из строки&lt;/span&gt;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, DTMF_Pause&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
                print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Установлено время паузы &amp;quot;&lt;/span&gt;, DTMF_Pause&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
            &lt;span class=&quot;co1&quot;&gt;-----------------------------------------------------&lt;/span&gt;
            &lt;span class=&quot;co1&quot;&gt;-- Выбор произвольной частоты&lt;/span&gt;
            &lt;span class=&quot;co1&quot;&gt;-----------------------------------------------------&lt;/span&gt;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, &lt;span class=&quot;st0&quot;&gt;&amp;quot;freq&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; true     &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
&amp;nbsp;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                 &lt;span class=&quot;co1&quot;&gt;-- Удаляем команду из строки&lt;/span&gt;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                 &lt;span class=&quot;co1&quot;&gt;-- Читаем номер канала&lt;/span&gt;
&amp;nbsp;
                &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; i &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
&amp;nbsp;
                    read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
                    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; i &lt;span class=&quot;sy0&quot;&gt;&amp;gt;&lt;/span&gt; 0 &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
&amp;nbsp;
                        SampleRate_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / i;
                        SampleRate_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; SampleRate_1 / NumOfSinPoints;
                        FreqEnable_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; true;
                        print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Частота канала 1 = &amp;quot;&lt;/span&gt;, i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
                        print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Sample rate = &amp;quot;&lt;/span&gt;, SampleRate_1&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
                    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
                &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; i &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
&amp;nbsp;
                    read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
                    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; i &lt;span class=&quot;sy0&quot;&gt;&amp;gt;&lt;/span&gt; 0 &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
&amp;nbsp;
                        SampleRate_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / i;
                        SampleRate_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; SampleRate_2 / NumOfSinPoints;
                        FreqEnable_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; true;
                        print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Частота канала 2 = &amp;quot;&lt;/span&gt;, i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
                    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
                &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;
                    print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;В команде freq неправильно задан номер канала: &amp;quot;&lt;/span&gt;, i, &lt;span class=&quot;st0&quot;&gt;&amp;quot;(должен быть 1 или 2)&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
                    read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
                &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
                &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;OK&amp;quot;&lt;/span&gt;;
&amp;nbsp;
            &lt;span class=&quot;co1&quot;&gt;-----------------------------------------------------&lt;/span&gt;
            &lt;span class=&quot;co1&quot;&gt;-- Прекратить генерацию обоих синусов&lt;/span&gt;
            &lt;span class=&quot;co1&quot;&gt;-----------------------------------------------------&lt;/span&gt;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, &lt;span class=&quot;st0&quot;&gt;&amp;quot;stop&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; true &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
&amp;nbsp;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                 &lt;span class=&quot;co1&quot;&gt;-- Удаляем команду из строки&lt;/span&gt;
                FreqEnable_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; false;
                FreqEnable_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; false;
&amp;nbsp;
            &lt;span class=&quot;co1&quot;&gt;-----------------------------------------------------&lt;/span&gt;
            &lt;span class=&quot;co1&quot;&gt;-- Задать уровень сигнала (в процентах)&lt;/span&gt;
            &lt;span class=&quot;co1&quot;&gt;-----------------------------------------------------&lt;/span&gt;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, &lt;span class=&quot;st0&quot;&gt;&amp;quot;level&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; true &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
&amp;nbsp;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                 &lt;span class=&quot;co1&quot;&gt;-- Удаляем команду из строки&lt;/span&gt;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
                &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; i &lt;span class=&quot;sy0&quot;&gt;&amp;gt;=&lt;/span&gt; 0 &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
                    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; i &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
                        SignalLevel &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; i;
                        print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Установлен уровень сигнала = &amp;quot;&lt;/span&gt;, SignalLevel, &lt;span class=&quot;st0&quot;&gt;&amp;quot;%&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
                    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
                &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
&amp;nbsp;
            &lt;span class=&quot;co1&quot;&gt;-----------------------------------------------------&lt;/span&gt;
            &lt;span class=&quot;co1&quot;&gt;-- Выдать DTMF-тон&lt;/span&gt;
            &lt;span class=&quot;co1&quot;&gt;-----------------------------------------------------&lt;/span&gt;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;
&amp;nbsp;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_Line, i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
                &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; i &lt;span class=&quot;sy0&quot;&gt;&amp;lt;&lt;/span&gt; 0 &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
                    print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Неправильный тон: &amp;quot;&lt;/span&gt;, i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
                &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; i &lt;span class=&quot;sy0&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;16&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
                    print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Неправильный тон: &amp;quot;&lt;/span&gt;, i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
                &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;
&amp;nbsp;
                    print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Генерируется тон: &amp;quot;&lt;/span&gt;, i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
                    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;    j &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;                   &lt;span class=&quot;co1&quot;&gt;-- 1&lt;/span&gt;
                        SampleRate_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;697&lt;/span&gt;;
                        SampleRate_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;1209&lt;/span&gt;;
                    &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; j &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;                   &lt;span class=&quot;co1&quot;&gt;-- 2&lt;/span&gt;
                        SampleRate_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;697&lt;/span&gt;;
                        SampleRate_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;1336&lt;/span&gt;;
                    &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; j &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;                   &lt;span class=&quot;co1&quot;&gt;-- 3&lt;/span&gt;
                        SampleRate_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;697&lt;/span&gt;;
                        SampleRate_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;1477&lt;/span&gt;;
                    &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; j &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;                   &lt;span class=&quot;co1&quot;&gt;-- 4&lt;/span&gt;
                        SampleRate_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;770&lt;/span&gt;;
                        SampleRate_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;1209&lt;/span&gt;;
                    &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; j &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;                   &lt;span class=&quot;co1&quot;&gt;-- 5&lt;/span&gt;
                        SampleRate_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;770&lt;/span&gt;;
                        SampleRate_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;1336&lt;/span&gt;;
                    &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; j &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;                   &lt;span class=&quot;co1&quot;&gt;-- 6&lt;/span&gt;
                        SampleRate_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;770&lt;/span&gt;;
                        SampleRate_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;1477&lt;/span&gt;;
                    &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; j &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;                   &lt;span class=&quot;co1&quot;&gt;-- 7&lt;/span&gt;
                        SampleRate_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;852&lt;/span&gt;;
                        SampleRate_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;1209&lt;/span&gt;;
                    &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; j &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;                   &lt;span class=&quot;co1&quot;&gt;-- 8&lt;/span&gt;
                        SampleRate_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;852&lt;/span&gt;;
                        SampleRate_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;1336&lt;/span&gt;;
                    &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; j &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;9&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;                   &lt;span class=&quot;co1&quot;&gt;-- 9&lt;/span&gt;
                        SampleRate_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;852&lt;/span&gt;;
                        SampleRate_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;1477&lt;/span&gt;;
                    &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; j &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;                  &lt;span class=&quot;co1&quot;&gt;-- 0&lt;/span&gt;
                        SampleRate_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;941&lt;/span&gt;;
                        SampleRate_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;1209&lt;/span&gt;;
                    &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; j &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;11&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;                  &lt;span class=&quot;co1&quot;&gt;-- *&lt;/span&gt;
                        SampleRate_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;941&lt;/span&gt;;
                        SampleRate_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;1336&lt;/span&gt;;
                    &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; j &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;                  &lt;span class=&quot;co1&quot;&gt;-- #&lt;/span&gt;
                        SampleRate_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;941&lt;/span&gt;;
                        SampleRate_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;1477&lt;/span&gt;;
                    &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; j &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;13&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;                  &lt;span class=&quot;co1&quot;&gt;-- A&lt;/span&gt;
                        SampleRate_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;697&lt;/span&gt;;
                        SampleRate_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;1633&lt;/span&gt;;
                    &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; j &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;14&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;                  &lt;span class=&quot;co1&quot;&gt;-- B&lt;/span&gt;
                        SampleRate_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;770&lt;/span&gt;;
                        SampleRate_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;1633&lt;/span&gt;;
                    &lt;span class=&quot;kw1&quot;&gt;elsif&lt;/span&gt; j &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;15&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;                  &lt;span class=&quot;co1&quot;&gt;-- C&lt;/span&gt;
                        SampleRate_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;852&lt;/span&gt;;
                        SampleRate_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;1633&lt;/span&gt;;
                    &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;                                &lt;span class=&quot;co1&quot;&gt;-- D&lt;/span&gt;
                        SampleRate_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;941&lt;/span&gt;;
                        SampleRate_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; sec / &lt;span class=&quot;nu0&quot;&gt;1633&lt;/span&gt;;
                    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
                    SampleRate_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; SampleRate_1 / NumOfSinPoints;
                    SampleRate_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; SampleRate_2 / NumOfSinPoints;
&amp;nbsp;
                    FreqEnable_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; true;               &lt;span class=&quot;co1&quot;&gt;-- Включаем звук&lt;/span&gt;
                    FreqEnable_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; true;
&amp;nbsp;
                    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; DTMF_Duration;
&amp;nbsp;
                    FreqEnable_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; false;              &lt;span class=&quot;co1&quot;&gt;-- Выключаем звук&lt;/span&gt;
                    FreqEnable_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; false;
&amp;nbsp;
                    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; DTMF_Pause;
&amp;nbsp;
                &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;; &lt;span class=&quot;co1&quot;&gt;-- while DTMF_Line != &amp;quot;&amp;quot;&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;; &lt;span class=&quot;co1&quot;&gt;-- while not eof&lt;/span&gt;
&amp;nbsp;
    file_close&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;DTMF_File&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;Работа скрипта успешно завершена&amp;quot;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;WORK&lt;/span&gt;;
&amp;nbsp;
&amp;nbsp;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;-------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-- Процесс формирования регистров ADRESH:ADRESL. Когда пользовательская&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-- программа устанавливает бит GODONE, этот процесс берет мгновенные&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-- значения сигнала, складывает их, умножает на переменную, показывающую&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-- уровень сигнала, и отправляет в регистры ADRESH:ADRESL, соблюдая&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-- выравнивание (левое или правое)&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
ADC&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; AnalogSignal &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;integer&lt;/span&gt;;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;until&lt;/span&gt; ADCON0.GO_nDONE &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;';  &lt;span class=&quot;co1&quot;&gt;-- Ждем, когда программа начнет чтение ADC&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; ADCON0.ADON &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;' &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; ADCON0.GO_nDONE &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; '&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;' &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;
            &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; ic;
        &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;;
&amp;nbsp;
        AnalogSignal &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; AnalogSignal_1/&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;  + AnalogSignal_2/&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;; &lt;span class=&quot;co1&quot;&gt;-- Сумма мгновенных&lt;/span&gt;
                                                              &lt;span class=&quot;co1&quot;&gt;-- значений сигналов&lt;/span&gt;
        AnalogSignal &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; AnalogSignal * SignalLevel; &lt;span class=&quot;co1&quot;&gt;-- Вычисление уровня сигнала&lt;/span&gt;
        AnalogSignal &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; AnalogSignal / &lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt;;         &lt;span class=&quot;co1&quot;&gt;-- (в процентах)&lt;/span&gt;
        AnalogSignal &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; AnalogSignal + &lt;span class=&quot;nu0&quot;&gt;512&lt;/span&gt;;         &lt;span class=&quot;co1&quot;&gt;-- Средняя точка (Vdd/2)&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; AnalogSignal &lt;span class=&quot;sy0&quot;&gt;&amp;lt;&lt;/span&gt; 0 &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;                    &lt;span class=&quot;co1&quot;&gt;-- Ограничение снизу&lt;/span&gt;
            AnalogSignal &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; 0;
        &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; AnalogSignal &lt;span class=&quot;sy0&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1023&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;                 &lt;span class=&quot;co1&quot;&gt;-- Ограничение сверху&lt;/span&gt;
            AnalogSignal &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1023&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; ADCON1.ADFM &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; '0' &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;                  &lt;span class=&quot;co1&quot;&gt;-- Левое выравнивание&lt;/span&gt;
            ADRESH &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; AnalogSignal /  &lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;;
            ADRESL &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; AnalogSignal * &lt;span class=&quot;nu0&quot;&gt;64&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;                                        &lt;span class=&quot;co1&quot;&gt;-- Правое выравнивание&lt;/span&gt;
            ADRESH &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; AnalogSignal / &lt;span class=&quot;nu0&quot;&gt;256&lt;/span&gt;;
            ADRESL &lt;span class=&quot;sy0&quot;&gt;&amp;lt;=&lt;/span&gt; AnalogSignal;
        &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;; &lt;span class=&quot;co1&quot;&gt;-- ADON&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; ADC;
&amp;nbsp;
&amp;nbsp;
&amp;nbsp;
&amp;nbsp;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;-------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-- Генерация синуса 1. Период выборки значений из таблицы синуса&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-- оределяется переменной SampleRate_1. Файл со значениями синуса&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-- читается по кругу.&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-- FreqEnable_1 разрешает/запрещает генерацию синуса&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
FREQ1&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;file&lt;/span&gt;     InFile_1 &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;text&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; Status_1 &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; file_open_status;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; InLine_1 &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;line&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; i_1      &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;integer&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; Time_1   &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;time&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; Now_1    &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;time&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
    file_open&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Status_1, InFile_1, SinusFileName, read_mode&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; Status_1 !&lt;span class=&quot;sy0&quot;&gt;=&lt;/span&gt; open_ok &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
        print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Ошибка открытия файла &amp;quot;&lt;/span&gt;, SinusFileName&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;Скрипт приостановлен&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;severity&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;failure&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; endfile&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InFile_1&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; false &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;           &lt;span class=&quot;co1&quot;&gt;-- Читаем весь файл&lt;/span&gt;
        readline&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InFile_1, InLine_1&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;               &lt;span class=&quot;co1&quot;&gt;-- построчно&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine_1, &lt;span class=&quot;st0&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; false &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;     &lt;span class=&quot;co1&quot;&gt;-- Выбираем значения из строки&lt;/span&gt;
            &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; FreqEnable_1 &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; true &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;            &lt;span class=&quot;co1&quot;&gt;-- Генерация разрешена&lt;/span&gt;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine_1, AnalogSignal_1&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
            &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;                                    &lt;span class=&quot;co1&quot;&gt;-- Генерация запрещена&lt;/span&gt;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine_1, i_1&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
                AnalogSignal_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; 0;
                &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; us;
            &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
            Now_1  &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; now&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                        &lt;span class=&quot;co1&quot;&gt;-- Компенсация ошибки, вносимой&lt;/span&gt;
            Time_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; Time_1 + SampleRate_1;        &lt;span class=&quot;co1&quot;&gt;-- несоответствием длительности&lt;/span&gt;
                                                    &lt;span class=&quot;co1&quot;&gt;-- машинного цикла и SampleRate&lt;/span&gt;
            &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;  Time_1  &lt;span class=&quot;sy0&quot;&gt;&amp;lt;&lt;/span&gt; Now_1 &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
                Time_1 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; Now_1;
            &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; Time_1 - Now_1;                  &lt;span class=&quot;co1&quot;&gt;-- Пауза, равная периоду выборки&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;;
&amp;nbsp;
    file_close&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InFile_1&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; FREQ1;
&amp;nbsp;
&amp;nbsp;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;-------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-- Генерация синуса 2. Период выборки значений из таблицы синуса&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-- оределяется переменной SampleRate_2. Файл со значениями синуса&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-- читается по кругу.&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-- FreqEnable_2 разрешает/запрещает генерацию синуса&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;-------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
FREQ2&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;is&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;file&lt;/span&gt;     InFile_2 &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;text&lt;/span&gt;;               &lt;span class=&quot;co1&quot;&gt;-- Файл со значениями синуса&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; Status_2 &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; file_open_status;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; InLine_2 &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;line&lt;/span&gt;;               &lt;span class=&quot;co1&quot;&gt;-- Рабочая строка для чтения файла&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; Time_2   &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;time&lt;/span&gt;;               &lt;span class=&quot;co1&quot;&gt;--&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; Now_2    &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;time&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;variable&lt;/span&gt; i_2      &lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;integer&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;begin&lt;/span&gt;
&amp;nbsp;
    file_open&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Status_2, InFile_2, SinusFileName, read_mode&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; Status_2 !&lt;span class=&quot;sy0&quot;&gt;=&lt;/span&gt; open_ok &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
        print&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot;Ошибка открытия файла &amp;quot;&lt;/span&gt;, SinusFileName&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;&amp;quot;Скрипт приостановлен&amp;quot;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;severity&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;failure&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; endfile&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InFile_2&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; false &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;           &lt;span class=&quot;co1&quot;&gt;-- Читаем весь файл&lt;/span&gt;
        readline&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InFile_2, InLine_2&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;               &lt;span class=&quot;co1&quot;&gt;-- построчно&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; match&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine_2, &lt;span class=&quot;st0&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; false &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;     &lt;span class=&quot;co1&quot;&gt;-- Выбираем значения из строки&lt;/span&gt;
            &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; FreqEnable_2 &lt;span class=&quot;sy0&quot;&gt;==&lt;/span&gt; true &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;            &lt;span class=&quot;co1&quot;&gt;-- Генерация разрешена&lt;/span&gt;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine_2, AnalogSignal_2&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
            &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;                                    &lt;span class=&quot;co1&quot;&gt;-- Генерация запрещена&lt;/span&gt;
                read&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InLine_2, i_2&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
                AnalogSignal_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; 0;
                &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt; us;
            &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
            Now_2  &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; now&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                        &lt;span class=&quot;co1&quot;&gt;-- Компенсация ошибки, вносимой&lt;/span&gt;
            Time_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; Time_2 + SampleRate_2;        &lt;span class=&quot;co1&quot;&gt;-- несоответствием длительности&lt;/span&gt;
                                                    &lt;span class=&quot;co1&quot;&gt;-- машинного цикла и SampleRate&lt;/span&gt;
            &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;  Time_2  &lt;span class=&quot;sy0&quot;&gt;&amp;lt;&lt;/span&gt; Now_2 &lt;span class=&quot;kw1&quot;&gt;then&lt;/span&gt;
                Time_2 &lt;span class=&quot;sy0&quot;&gt;:=&lt;/span&gt; Now_2;
            &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt;;
&amp;nbsp;
            &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; Time_2 - Now_2;                &lt;span class=&quot;co1&quot;&gt;-- Пауза, равная периоду выборки&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;loop&lt;/span&gt;;
&amp;nbsp;
    file_close&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;InFile_2&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;process&lt;/span&gt; FREQ2;
&amp;nbsp;
&amp;nbsp;
&lt;span class=&quot;kw1&quot;&gt;end&lt;/span&gt; testbench;&lt;/pre&gt;
&lt;p&gt;
Он, хоть и сравнительно большой, но понятный. Не составит труда дабавить еще один процесс, который будет подмешивать к сигналу шум. Для этого нужно будет воспользоваться функцией &lt;span class=&quot;important&quot;&gt;&lt;a href=&quot;#время&quot; title=&quot;osa:articles:scl &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;random_time&lt;/a&gt;&lt;/span&gt;, а в процессе &lt;span class=&quot;important&quot;&gt;ADC&lt;/span&gt; вычислять сумму не двух сигналов, а трех. Уровень шума удобно будет регулировать через входной файл.
&lt;/p&gt;

&lt;p&gt;
Ниже приведен фрагмент графика сигнала, сформированного данным скриптом для команды &amp;quot;1&amp;quot; (697 Гц + 1209 Гц):
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/graph_dtmf.png?id=osa%3Aarticles%3Ascl&quot; class=&quot;media&quot; title=&quot;osa:articles:graph_dtmf.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/graph_dtmf.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Генератор DTMF&quot; [79268-106128] --&gt;
&lt;h1&gt;&lt;a name=&quot;заключение&quot; id=&quot;заключение&quot;&gt;Заключение&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;/div&gt;
&lt;div class=&quot;level5&quot;&gt;

&lt;p&gt;
Итак, в статье были описаны известные мне конструкции языка SCL, а также приведены несколько небольших примеров использования этого языка для имитации цифровых и аналоговых сигналов в симуляторе. Я надеюсь, что описанное здесь поможет упростить процесс отладки, а главное - поможет вам утвердиться во мнении, что симулятор в MPLAB IDE - это действительно мощное средство, и не такое оно неудобное, как может показаться, если пользоваться только диалоговым окном Stimulus, пренебрегая самим языком SCL. Так что теперь, я думаю, такие задачи, как имитация 128-битного манчестерского кода, трудностей больше не вызовут.
&lt;/p&gt;

&lt;p&gt;
Тем, кто раньше пользовался диалоговым окном &lt;span class=&quot;important&quot;&gt;Stimulus&lt;/span&gt; для задания входных сигналов, а после прочтения статьи задумался: &amp;quot;А как бы мне то же самое сделать на SCL?&amp;quot; - напомню, что в MPLAB IDE есть возможность сформировать файл SCL для заданных в диалоговом окне сигналов. Для этого нужно в диалоговом окне &amp;quot;Stimulus&amp;quot; нажать кнопку &amp;quot;Advanced…&amp;quot; и в открывшемся окне нажать кнопку &amp;quot;Generate SCL File&amp;quot;. Все заданные вами воздействия будут автоматически переведены в скрипт на языке SCL.
&lt;/p&gt;

&lt;p&gt;
Виктор Тимофеев, ноябрь, 2009&lt;br/&gt;
 
&lt;a href=&quot;mailto:&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot; class=&quot;mail JSnocheck&quot; title=&quot;&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot;&gt;osa@pic24.ru&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Заключение&quot; [106129-] --&gt;</description>
    </item>
    <item rdf:about="http://www.pic24.ru/doku.php/osa/articles/vga_game?rev=1448561546">
        <dc:format>text/html</dc:format>
        <dc:date>2015-11-26T21:12:26+03:00</dc:date>
        <title>Видеоигра на PIC18</title>
        <link>http://www.pic24.ru/doku.php/osa/articles/vga_game?rev=1448561546</link>
        <description>


&lt;h1&gt;&lt;a name=&quot;видеоигра_на_pic18&quot; id=&quot;видеоигра_на_pic18&quot;&gt;Видеоигра на PIC18&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Видеоигра на PIC18&quot; [1-45] --&gt;
&lt;h2&gt;&lt;a name=&quot;введение&quot; id=&quot;введение&quot;&gt;Введение&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Можно поставить под сомнение эффективность использования микроконтроллеров PIC для формирования сигналов управления монитором VGA, но нельзя отрицать того, что такое их применение возможно. Данный пример демонстрирует применение микроконтроллера PIC18F2550 в качестве самостоятельной игровой приставки. В 2004-м году была написана &lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/vga_terminal&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:vga_terminal&quot;&gt;программа-терминал&lt;/a&gt;, позволяющая использовать PIC18 для формирования цветного текстового изображения на мониторе VGA. Я немного расширил возможности этой программы, добавив анимацию, полифонический звук и кнопочное управление. В далеких 80-х, когда у меня была игровая приставка &amp;quot;Синклер&amp;quot; я был без ума от игрушки Boulder Dash фирмы &lt;a href=&quot;http://www.firststarsoftware.com&quot; class=&quot;urlextern&quot; title=&quot;http://www.firststarsoftware.com&quot;  rel=&quot;nofollow&quot;&gt;First Star Software&lt;/a&gt;. Сейчас я сделал попытку перенести ее на ПИК. Что из этого вышло, - выкладываю здесь с исходниками.
&lt;/p&gt;

&lt;p&gt;
Исходные тексты программы: &lt;a href=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/vga_game.rar&quot; class=&quot;media mediafile mf_rar&quot; title=&quot;osa:articles:vga_game.rar&quot;&gt;vga_game.rar&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Программа написана на Си (HT-PICC18), за исключением обработчика прерывания, который целиком написан на ассемблере. Выполнением программы управляет ОСРВ OSA.
&lt;/p&gt;

&lt;p&gt;
Характеристики программы:
&lt;/p&gt;
&lt;table class = &quot;fpl&quot;&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;strong&gt;Контроллер&lt;/strong&gt; 		&lt;/td&gt;
		&lt;td&gt;
PIC18F2550		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;strong&gt;Таковая частота&lt;/strong&gt;		&lt;/td&gt;
		&lt;td&gt;
48 &lt;acronym title=&quot;Megahertz&quot;&gt;MHz&lt;/acronym&gt; (12 MIPS)		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;strong&gt;RTOS&lt;/strong&gt;		&lt;/td&gt;
		&lt;td&gt;
OSA		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;strong&gt;VGA&lt;/strong&gt;		&lt;/td&gt;
		&lt;td&gt;
256x200 пикселей, 15 цветов		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;strong&gt;Полифония&lt;/strong&gt;		&lt;/td&gt;
		&lt;td&gt;
5 голосов (4 - музыка, 1 - игровые эффекты). Частота семплирования 15 КГц		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;strong&gt;Игровое поле&lt;/strong&gt;		&lt;/td&gt;
		&lt;td&gt;
40x20 клеток		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;strong&gt;Видимая область игрового поля&lt;/strong&gt;		&lt;/td&gt;
		&lt;td&gt;
16x12 клеток		&lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Введение&quot; [46-2417] --&gt;
&lt;h2&gt;&lt;a name=&quot;видео_hq_34_mb&quot; id=&quot;видео_hq_34_mb&quot;&gt;Видео HQ (34 Mb)&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Вот небольшое видео. Качество не очень хорошее, т.к. снималось на дешевую web-камеру (звук писался отдельно через линейный вход).
&lt;/p&gt;

&lt;p&gt;
&lt;div align=center&gt;
&lt;object width=&quot;640&quot; height=&quot;480&quot;&gt;
&lt;param name=&quot;movie&quot; value=&quot;http://www.youtube.com/v/PZjuyCuH7Ac&amp;hl=en&amp;fs=1&amp;rel=0&quot;&gt;&lt;/param&gt;
&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;&gt;&lt;/param&gt;

&lt;embed src=&quot;http://www.youtube.com/v/PZjuyCuH7Ac&amp;hl=en&amp;fs=1&amp;rel=0&quot; type=&quot;application/x-shockwave-flash&quot; allowfullscreen=&quot;true&quot; width=&quot;640&quot; height=&quot;480&quot;&gt;&lt;/embed&gt;

&lt;/object&gt;
&lt;/div&gt;
&lt;br/&gt;
 
&lt;br/&gt;
 
&lt;br/&gt;
 
&lt;br/&gt;
 
К сожалению, в лучшем качестве снять не удалось. На самом деле изображение гораздо четче:&lt;br/&gt;
 
&lt;br/&gt;
 
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/vga_game_big_plan.jpg?id=osa%3Aarticles%3Avga_game&quot; class=&quot;media&quot; title=&quot;osa:articles:vga_game_big_plan.jpg&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/vga_game_big_plan.jpg&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Видео HQ (34 Mb)&quot; [2418-3301] --&gt;
&lt;h2&gt;&lt;a name=&quot;описание&quot; id=&quot;описание&quot;&gt;Описание&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Описание&quot; [3302-3331] --&gt;
&lt;h3&gt;&lt;a name=&quot;схема&quot; id=&quot;схема&quot;&gt;Схема&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Схема устройства довольно простая:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/vga_game_scheme.png?id=osa%3Aarticles%3Avga_game&quot; class=&quot;media&quot; title=&quot;osa:articles:vga_game_scheme.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/vga_game_scheme.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Логически схему можно разбить на 4 части:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Сердце устройства - микроконтроллер;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; 3 резистора и 6 диодов для формирования цветного изображения;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; фильтр низких частот второго порядка для вывода звука;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; пять кнопок с резистивной подтяжкой к +5В.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Принцип формирования изображения описан &lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/vga_terminal#генерация_изображения&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:vga_terminal&quot;&gt;здесь&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
А вот фото самого устройства:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/vga_boulder_dash_device.jpg?id=osa%3Aarticles%3Avga_game&quot; class=&quot;media&quot; title=&quot;osa:articles:vga_boulder_dash_device.jpg&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/vga_boulder_dash_device.jpg&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Схема&quot; [3332-4172] --&gt;
&lt;h3&gt;&lt;a name=&quot;распределение_ресурсов&quot; id=&quot;распределение_ресурсов&quot;&gt;Распределение ресурсов&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Как видно из характеристик программы, контроллеру приходится успевать делать довольно много дел одновременно и четко синхронно: генерация синхроимпульсов, генерация RGB-сигналов, генерация звука - все эти задачи контроллер должен делать синхронно с точностью до такта, иначе картинка будет дрожать, цвета - прыгать влево-вправо, звук - дребезжать. Да и игровые функции хотелось бы обрабатывать в реальном времени: обработка кнопок, формирование данных для анимации, игровой процесс, воспроизведение музыки и игровых эффектов.
&lt;/p&gt;

&lt;p&gt;
Было решено сделать так: часть задач, особо критичных ко времени, затолкать в прерывание, а все остальные задачи, допускающие отклонение в несколько сотен тактов контроллера, оформить в виде функций.
&lt;/p&gt;

&lt;p&gt;
На обработчик прерывания возлагаются следующие задачи:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; формирование VGA-синхроимпульсов;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; формирование RGB-сигналов;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; синтезатор звуков;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; чтение кнопок и устранение дребезга.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Вне прерывания будет выполняться все остальное:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; формирование данных анимации;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; игровой алгоритм;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; формирование данных для синтезатора (музыка и игровые эффекты).&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Учитывая, что обработчик прерывания функционально сильно перегружен, весь его код полностью написан на ассемблере. Это позволяет не только с точностью до такта синхронизировать задачу формирования VGA-сигналов, но и максимально оптимизировать код вручную так, как компилятору будет сделать не по силам. Например, при прорисовке игрового поля и текстовой строки программа загружена по-разному и имеет свободное время для обработки звука в разные моменты времени. Поэтому код обработки звука разбит на несколько маленьких кусочков, которые втиснуты в &amp;quot;щели&amp;quot;, образующиеся при формировании изображения. Например, на прорисовку каждой клеточки игрового поля уходит ровно 14 команд, а время прорисовки - 16 тактов, следовательно, есть 2 свободных такта. Прорисовка сделана так, что нечетные клеточки имеют эти два свободных такта в конце прорисовки, а четные - в начале, и т.к. они идут последовательно, то на стыках &amp;quot;нечетная-четная&amp;quot; (а таких стыков 8) появляются &amp;quot;щели&amp;quot; по 4 такта (в общей сложности 32 такта), куда и втискиваются части кода для формирования звука. При прорисовке текстовой строки эти &amp;quot;щели&amp;quot; образуются в других местах. Использование ассемблера позволило утрамбовать код так, чтобы программа успевала сделать все за выделенные ей 380 тактов (380 * 83.3 нс = 31.7 мкс - период горизонтального синхроимпульса)
&lt;/p&gt;

&lt;p&gt;
Кроме того, нам нужно помнить, что для выполнения всех остальных задач остается совсем немного времени. Фактически во время прорисовки 400 строк видеоизображения у контроллера нет ни такта свободного времени для выполнения второстепенных задач, т.к. все остальное время (время синхроимпульса и левого и правого бордюра) тратится на управление синтезатором и подготовку данных для вывода на экран, а также на сохранение/восстановление контекста прерывания. Таким образом, из всего времени, за которое прорисовываются 525 строк одного кадра, есть всего 125, во время обработки которых контроллер более или менее разгружен (в это время он занят только синтезатором). Следовательно, нам нужен механизм, который позволил бы наиболее эффективно распределять оставшееся время между остальными задачами. 
&lt;/p&gt;

&lt;p&gt;
И здесь нам на помощь приходит RTOS. Дело в том, что, например, просчет одного игрового шага (траектории движения всяких &amp;quot;бабочек&amp;quot; и &amp;quot;огневушек&amp;quot;, падение камней и алмазов и пр.) может длиться сравнительно долго. Из-за этого музыка, которая играется по фону, генерировалась бы со срывами (т.к. во время просчета игрового алгоритма программа иногда будет полностью уходить в прерывания на прорисовку 400 линий растра, что будет затягивать время просчета на долгие миллисекунды). RTOS же позволила прерывать длительные расчеты для того, чтобы сформировать указания синтезатору для воспроизведения очередных нот. Для этого нужно было сделать всего две вещи:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; задаче, проигрывающей мелодию, установить более высокий приоритет;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; из задачи расчета игрового шага периодически передавать управление ядру ОС.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Распределение ресурсов&quot; [4173-11490] --&gt;
&lt;h3&gt;&lt;a name=&quot;формирование_изображения&quot; id=&quot;формирование_изображения&quot;&gt;Формирование изображения&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Формирование изображения практически такое же, как и в проекте &lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/vga_terminal&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:vga_terminal&quot;&gt;&amp;quot;Терминал&amp;quot;&lt;/a&gt;. Разница состоит в том, что на один пиксель выделяется две строки растра, а размер спрайта не 8x16 пикселей, а 16x16. Т.е. вся информация выводится в виде двухцветных матриц (один из цветов всегда черный). Эффект анимации создается за счет быстрой смены матриц. Пои прорисовке очередной строки из массива спрайтов, расположенного в ROM, программа выбирает байты, соответствующие текущему номеру строки. На каждый спрайт по два байта в одной строке. 
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Формирование изображения&quot; [11491-12519] --&gt;
&lt;h3&gt;&lt;a name=&quot;синтезатор&quot; id=&quot;синтезатор&quot;&gt;Синтезатор&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Синтезатор построен по тому же принципу, что и в программе &lt;a href=&quot;http://www.pic24.ru/doku.php/osa/ref/appendix/quartet&quot; class=&quot;wikilink1&quot; title=&quot;osa:ref:appendix:quartet&quot;&gt;&amp;quot;Квартет&amp;quot;&lt;/a&gt;. Т.е. на каждый канал заведена переменная типа структуры, содержащей информацию о частоте, текущей фазе, громкости и пр. для данного канала. Исходя из данных содержащихся в этой структуре, выбирается байт из массива, в котором хранится оцифрованный период синусоиды (на самом деле это не совсем синусоида, а функция, вычисленная как (6*sin(x)+3*sin(2x)+sin(3x))/10). Сумма мгновенных значений амплитуд для всех каналов выводится через ШИМ. Одновременно пересчитывается громкость, затухание, для игрового канала - шум, сдвиг частоты, затухание шума. (Единственное отличие от синтезатора из &amp;quot;Квартета&amp;quot; - это добавление для канала игровых звуков эффекта шума.)
&lt;/p&gt;

&lt;p&gt;
Синтезатор формирует мгновенные значения амплитуд для всех каналов в два прохода (т.е. за время прорисовки двух строк растра): на четных строках просчитываются амплитуды для каналов 0,1 и 2, а на нечетных - 3 и 4 (игровой). Поэтому и частота семплирования в два раза ниже частоты горизонтальных синхроимпульсов и равна 15.74 КГц.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Синтезатор&quot; [12520-14463] --&gt;
&lt;h3&gt;&lt;a name=&quot;музыка&quot; id=&quot;музыка&quot;&gt;Музыка&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Музыка также организована по принципу, схожему с программой &lt;a href=&quot;http://www.pic24.ru/doku.php/osa/ref/appendix/quartet&quot; class=&quot;wikilink1&quot; title=&quot;osa:ref:appendix:quartet&quot;&gt;&amp;quot;Квартет&amp;quot;&lt;/a&gt;. На каждый звуковой канал заведена своя &amp;quot;нотная тетрадь&amp;quot;, откуда по очереди берутся и проигрываются ноты, выдерживаются паузы, выполняются повторы фрагментов. Но здесь, в отличие от &amp;quot;Квартета&amp;quot;, на каждый звуковой канал не выделена своя задача, а все они обрабатываются в одной задаче - &lt;strong&gt;Task_Music&lt;/strong&gt;.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Музыка&quot; [14464-15178] --&gt;
&lt;h3&gt;&lt;a name=&quot;анимация&quot; id=&quot;анимация&quot;&gt;Анимация&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Т.к. игра содержит анимированные объекты: сам человечек, бабочки, огневушки, алмазы, домик, взрывы, - то требуется с определенным периодом обновлять данные, чтобы код прорисовки, расположенный в обработчике прерывания, знал, какой спрайт для данного объекта нужно выбрать. Эта задача проходится в цикле по всем восьмистам клеткам игрового поля и обновляет у всех анимированных объектов два младших бита, отвечающих за фазу анимации (т.е. на каждый анимированный объект предусмотрено 4 фазы движения). Т.к. данная задача может выполняться длительное время, то после обработки каждых сорока клеток задача возвращает управление планировщику.
&lt;/p&gt;

&lt;p&gt;
Задача обработки анимации &lt;strong&gt;Task_Animate&lt;/strong&gt; запускается после прорисовки каждого 8-го кадра (т.е. 7.5 раз в секунду).
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Анимация&quot; [15179-16568] --&gt;
&lt;h3&gt;&lt;a name=&quot;игра&quot; id=&quot;игра&quot;&gt;Игра&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Это была самая простая часть программы. Задача обработки игрового алгоритма &lt;strong&gt;Task_Game&lt;/strong&gt; активируется после прорисовки каждого 16-го кадра (4 раза в секунду) и выполняет один шаг обновления игрового поля. Пробегаясь по всем его клеткам, задача ищет объекты, которые должны двигаться, вычисляет для них, в зависимости от траектории, следующее местоположение, а также следит за правилами игры: например, столкновение движущегося объекта (человечка или бабочки) с летящим камнем должно приводить к взрыву; взрыв уничтожает все в радиусе 2 клеток, кроме титановой стены; бабочка после смерти превращается в несколько алмазов и т.д.
&lt;/p&gt;

&lt;p&gt;
Так как задача выполняется довольно длительное время, она иногда (после проверки каждых сорока клеток) передает управление планировщику, чтобы задача формирования музыки или обработки звуковых эффектов смогли получить управление. После проверки и обработки всех восьмисот клеток игрового поля &lt;strong&gt;Task_Game&lt;/strong&gt; отправляет короткое сообщение задаче &lt;strong&gt;Task_Sound&lt;/strong&gt; с кодом звука, который должен сопроводить данный шаг игры, например, звук взрыва, или падение камня или поедание алмаза. Если данный шаг не должен сопровождаться никакими звуками, то сообщение не отсылается.
&lt;/p&gt;

&lt;p&gt;
Возможно, при написании алгоритма игры где-то есть несоответствие с оригинальными правилами (например, в порядке падения камней друг на друга, когда они валят с разных сторон), но внешне все получилось очень похоже. В моей версии игры нет еще четырех эффектов, присущих обычному Boulder Dash (я использую терминологию, которой пользовался в детстве): растущая биомасса (для убийства бабочек), не растущая биомасса (пропускающая камни и алмазы с задержкой), растущая кирпичная стена (заполняет пустые пространства влево и вправо), золотая стена (превращает камни в алмазы и наоборот). Если честно, то я просто поленился. 
&lt;/p&gt;

&lt;p&gt;
Управление: 
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; кнопками &amp;quot;влево&amp;quot;, &amp;quot;вправо&amp;quot;, &amp;quot;вверх&amp;quot; и &amp;quot;вниз&amp;quot; двигаем человечка; &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; комбинация любой из этих кнопок с кнопкой &amp;quot;Fire&amp;quot; делает шаг в соответствующую сторону без перемещения человечка (например, съесть алмаз, находящийся справа, оставшись при этом стоять на месте);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; если так получилось, что человечек оказался завален камнями и ему не сделать шаг ни в одну сторону, то можно нажать все 4 кнопки направления сразу - это приведет к перезагрузке уровня;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; нажатием всех пяти кнопок сразу мы переходим на другой уровень.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Игра&quot; [16569-20813] --&gt;
&lt;h3&gt;&lt;a name=&quot;создание_новых_уровней&quot; id=&quot;создание_новых_уровней&quot;&gt;Создание новых уровней&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

За вами остается возможность создавать свои игровые уровни. В файле game_data.c инициализируется массив &lt;strong&gt;const char MAP[][][]&lt;/strong&gt;, в котором хранятся карты игровых полей. Количество карт определяется константой &lt;strong&gt;NUMBER_OF_MAPS&lt;/strong&gt; (в моей программе = 3, но можно увеличить и создать больше карт). Каждая карта представляет собой массив char&amp;#039;ов размерностью (MAP_SIZE_Y+1)x(MAP_SIZE_X+1). Первая строка содержит определения цветов для разных типов объекта для конкретной карты (см. константы CL_xxx),  а также требуемое количество алмазов для прохождения уровня. Далее следуют MAP_SIZE_Y строк, в которых задается сама карта с описанием объектов:
&lt;/p&gt;

&lt;p&gt;
(пробел) - пустота;&lt;br/&gt;
 
&lt;strong&gt;#&lt;/strong&gt; титановая не ломаемая стена;&lt;br/&gt;
 
&lt;strong&gt;=&lt;/strong&gt; - кирпичная стена;&lt;br/&gt;
 
&lt;strong&gt;.&lt;/strong&gt; - земля;&lt;br/&gt;
 
&lt;strong&gt;m&lt;/strong&gt; - человечек;&lt;br/&gt;
 
&lt;strong&gt;h&lt;/strong&gt; - домик;&lt;br/&gt;
 
&lt;strong&gt;x&lt;/strong&gt; - бабочка;&lt;br/&gt;
 
&lt;strong&gt;f&lt;/strong&gt; - огневушка;&lt;br/&gt;
 
&lt;strong&gt;o&lt;/strong&gt; - камень;&lt;br/&gt;
 
&lt;strong&gt;+&lt;/strong&gt; - алмаз.&lt;br/&gt;
 
&lt;/p&gt;

&lt;p&gt;
Бордюр карты должен состоять из титановой стены. По самому исходному файлу довольно просто разобраться, что там к чему и как что менять, чтобы создавать свои карты. При небольшом усилии и небольшой модификации программы можно сделать подгружаемые карты из внешней EEPROM.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Создание новых уровней&quot; [20814-22773] --&gt;
&lt;h2&gt;&lt;a name=&quot;сборка_проекта&quot; id=&quot;сборка_проекта&quot;&gt;Сборка проекта&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Проект создан в интегрированной среде &lt;a href=&quot;http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&amp;amp;nodeId=1406&amp;amp;dDocName=en019469&amp;amp;part=SW007002&quot; class=&quot;urlextern&quot; title=&quot;http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&amp;amp;nodeId=1406&amp;amp;dDocName=en019469&amp;amp;part=SW007002&quot;  rel=&quot;nofollow&quot;&gt;MPLAB IDE&lt;/a&gt;. Для сборки использовался компилятор HT-PICC18 STD 9.51pl2.
&lt;/p&gt;

&lt;p&gt;
Скачиваем &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/doku.php/osa/ref/download/intro&quot; class=&quot;wikilink1&quot; title=&quot;osa:ref:download:intro&quot;&gt;файлы операционной системы OSA&lt;/a&gt;&lt;/strong&gt;, распаковываем архив на диск C: (должна получиться папка C:\OSA).
&lt;/p&gt;

&lt;p&gt;
Распаковываем файл &lt;strong&gt;&lt;a href=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/vga_game.rar&quot; class=&quot;media mediafile mf_rar&quot; title=&quot;osa:articles:vga_game.rar&quot;&gt;vga_game.rar&lt;/a&gt;&lt;/strong&gt; в папку C:\TEST\VGA. При этом внутри создастся папка VGA_GAME. В MPLAB IDE открываем проект.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Примечание.&lt;/strong&gt;&lt;em&gt; При распаковке в другую папку, отличную от C:\TEST\VGA\VGA_GAME, нужно будет через меню Project\Build options…\Project в закладке Directories в списке include-путей заменить путь к файлам проекта на тот, куда Вы распаковали файлы из архива.&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
Выполняем сборку нажатием &lt;strong&gt;Ctrl+F10&lt;/strong&gt;.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Сборка проекта&quot; [22774-24007] --&gt;
&lt;h2&gt;&lt;a name=&quot;заключение&quot; id=&quot;заключение&quot;&gt;Заключение&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Создавая данный проект, я преследовал несколько целей:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Во-первых, просто упражнялся в оптимизации кода; &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Во-вторых, всегда интересно, что можно выжать из контроллера; &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; В-третьих, этот проект является очередным примером использования RTOS, причем в данном случае ее применение более чем оправдано, т.к. из-за огромных временных затрат на прорисовку изображения создается дефицит временных ресурсов контроллера, который ОС и позволяет использовать с максимальной эффективностью, исключая протормаживания и зависания.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Само устройство можно совершенствовать дальше. Например, применив внешнюю SRAM-память, можно расширить графические возможности (и количество цветов и избавиться от ограничения &amp;quot;1 цвет на спрайт&amp;quot;). Повесив внешний музыкальный контроллер (пускай просто второй ПИК), можно значительно расширить музыкальные возможности устройства (например, повысить частоту семплирования, увеличить разрядность ШИМ, увеличить количество звуковых каналов, добавить имитацию различных музыкальных инструментов и барабанов, сделать стерео и т.д.). Можно добавить внешнюю EEPROM для подгрузки карт, спрайтов, музыки и сохранения игр. Разгрузив контроллер, оставив на нем только правила игры и VGA-вывод (что, собственно, тоже можно разнести по разным контроллерам), мы можем усложнять алгоритм игры, добавлять интересные видеоэффекты и т.д. и т.п. 
&lt;/p&gt;

&lt;p&gt;
Если у кого-то возникнет желание сделать для себя (или не для себя) интересную поделку, - не стесняйтесь пользоваться моими наработками и исходниками хоть в любительских целях, хоть в коммерческих.
&lt;/p&gt;

&lt;p&gt;
Удачи!
&lt;/p&gt;

&lt;p&gt;
Виктор Тимофеев, май, 2009
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;mailto:&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot; class=&quot;mail JSnocheck&quot; title=&quot;&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot;&gt;osa@pic24.ru&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Заключение&quot; [24008-26933] --&gt;
&lt;h2&gt;&lt;a name=&quot;ссылки&quot; id=&quot;ссылки&quot;&gt;Ссылки&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Вот проекты, заслуживающие внимания:

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://bdash.gpio.dk/&quot; class=&quot;urlextern&quot; title=&quot;http://bdash.gpio.dk/&quot;  rel=&quot;nofollow&quot;&gt;http://bdash.gpio.dk/&lt;/a&gt; - более близкая к оригиналу игра на ATMega&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://avga.prometheus4.com/&quot; class=&quot;urlextern&quot; title=&quot;http://avga.prometheus4.com/&quot;  rel=&quot;nofollow&quot;&gt;http://avga.prometheus4.com/&lt;/a&gt; - несколько цветных видеоигр с выводом на VGA (ATmega).&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://www.linusakesson.net/scene/craft/&quot; class=&quot;urlextern&quot; title=&quot;http://www.linusakesson.net/scene/craft/&quot;  rel=&quot;nofollow&quot;&gt;http://www.linusakesson.net/scene/craft/&lt;/a&gt; - очень интересная демка с анимацией и полифонией (ATmega).&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://www.rickard.gunee.com/projects/&quot; class=&quot;urlextern&quot; title=&quot;http://www.rickard.gunee.com/projects/&quot;  rel=&quot;nofollow&quot;&gt;http://www.rickard.gunee.com/projects/&lt;/a&gt; - цветные игры &amp;quot;Тетрис&amp;quot; и &amp;quot;Pong&amp;quot; с выводом на телевизор (Scenix)&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://www.belogic.com/uzebox/index.htm&quot; class=&quot;urlextern&quot; title=&quot;http://www.belogic.com/uzebox/index.htm&quot;  rel=&quot;nofollow&quot;&gt;http://www.belogic.com/uzebox/index.htm&lt;/a&gt; - видеосистема на ATmega644: 240x224 пикселей, 256 цветов. Можно встроить в свою программу.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

И еще ссылки:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://z80speccy.narod.ru/&quot; class=&quot;urlextern&quot; title=&quot;http://z80speccy.narod.ru/&quot;  rel=&quot;nofollow&quot;&gt;http://z80speccy.narod.ru/&lt;/a&gt; - отличный эмулятор ZX Spectrum&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;a href=&quot;http://andygame.narod.ru/zx/game/index.html&quot; class=&quot;urlextern&quot; title=&quot;http://andygame.narod.ru/zx/game/index.html&quot;  rel=&quot;nofollow&quot;&gt;http://andygame.narod.ru/zx/game/index.html&lt;/a&gt; - игрушки для него (в том числе и Boulder Dash)&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

(Если у кого-то есть еще интересные ссылки по использованию 8-разрядников для формирования VGA-изображений, - пришлите, пожалуйста. Заслуживающие внимания я добавлю в список).
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Ссылки&quot; [26934-] --&gt;</description>
    </item>
    <item rdf:about="http://www.pic24.ru/doku.php/osa/articles/vga_terminal?rev=1308416534">
        <dc:format>text/html</dc:format>
        <dc:date>2011-06-18T21:02:14+03:00</dc:date>
        <title>Текстовый VGA-терминал на PIC18</title>
        <link>http://www.pic24.ru/doku.php/osa/articles/vga_terminal?rev=1308416534</link>
        <description>


&lt;h1&gt;&lt;a name=&quot;текстовый_vga-терминал_на_pic18&quot; id=&quot;текстовый_vga-терминал_на_pic18&quot;&gt;Текстовый VGA-терминал на PIC18&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Текстовый VGA-терминал на PIC18&quot; [1-66] --&gt;
&lt;h2&gt;&lt;a name=&quot;введение&quot; id=&quot;введение&quot;&gt;Введение&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Вашему вниманию предлагается программа для PIC18, в которой реализована функция простой видеокарты для вывода текстовой информации на VGA-дисплей. Конечно, PIC18 не совсем подходит для решения подобных задач, тем не менее, данный пример показывает, что это не невозможно. Эта программа была написана в 2004 году, и здесь она приводится для пояснения принципов работы проекта &lt;a href=&quot;http://www.pic24.ru/doku.php/osa/articles/vga_game&quot; class=&quot;wikilink1&quot; title=&quot;osa:articles:vga_game&quot;&gt;&amp;quot;Видеоигра на PIC18&amp;quot;&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
Исходные тексты программы терминала можно взять здесь: &lt;a href=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/terminal.rar&quot; class=&quot;media mediafile mf_rar&quot; title=&quot;osa:articles:terminal.rar&quot;&gt;terminal.rar&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Итак, в статье описана программа, реализующая функции видеокарты со следующими характеристиками:

&lt;/p&gt;
&lt;table class = &quot;fpl&quot;&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;strong&gt;Текстовое разрешение&lt;/strong&gt;		&lt;/td&gt;
		&lt;td&gt;
30x30 символов		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;strong&gt;Графическое разрешение&lt;/strong&gt;		&lt;/td&gt;
		&lt;td&gt;
240x480 пикселей		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;strong&gt;Размер матрицы знакогенератора&lt;/strong&gt;		&lt;/td&gt;
		&lt;td&gt;
8x16 пикселей		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;strong&gt;Количество цветов&lt;/strong&gt;		&lt;/td&gt;
		&lt;td&gt;
15		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;strong&gt;Эмуляция курсора&lt;/strong&gt;		&lt;/td&gt;
		&lt;td&gt;
Есть. С возможностью изменения размера.		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;strong&gt;Наборы символов&lt;/strong&gt;		&lt;/td&gt;
		&lt;td&gt;
Латинский, кириллица, псевдографика		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;strong&gt;Интерфейс управления&lt;/strong&gt;		&lt;/td&gt;
		&lt;td&gt;
SPI, до 250 кбит/с		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;strong&gt;&lt;/strong&gt;		&lt;/td&gt;
		&lt;td&gt;
		&lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;

Хоть видеокарта и способна отображать 15 цветов, но есть некоторые ограничения: 
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; фон может быть только черным;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; один символ может быть только одного цвета (не считая цвета фона).&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Вот тестовая картинка, сгенерированная видеокартой (сама программа была написана еще в 2004 году, но функция демо-экрана была добавлена только что для демонстрации):
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/vga_trm_demo_screen.jpg?id=osa%3Aarticles%3Avga_terminal&quot; class=&quot;media&quot; title=&quot;osa:articles:vga_trm_demo_screen.jpg&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/vga_trm_demo_screen.jpg?w=350&quot; class=&quot;media&quot; alt=&quot;&quot; width=&quot;350&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/vga_trm_large_plan.jpg?id=osa%3Aarticles%3Avga_terminal&quot; class=&quot;media&quot; title=&quot;osa:articles:vga_trm_large_plan.jpg&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/vga_trm_large_plan.jpg?w=350&quot; class=&quot;media&quot; alt=&quot;&quot; width=&quot;350&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Введение&quot; [67-2480] --&gt;
&lt;h2&gt;&lt;a name=&quot;управление_vga&quot; id=&quot;управление_vga&quot;&gt;Управление VGA&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Управление VGA&quot; [2481-2517] --&gt;
&lt;h3&gt;&lt;a name=&quot;немного_теории&quot; id=&quot;немного_теории&quot;&gt;Немного теории&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

VGA-монитор управляется по 5 линиям: 2 цифровых (синхронизация горизонтальная и вертикальная) и 3 аналоговых (красный, зеленый, синий). Линии синхронизации служат для того, чтобы монитор знал, где начало кадра, какое разрешение и какова частота кадров. Аналоговые входы служат для задания цвета. Чем выше напряжение мы подаем на аналоговый вход, тем более яркий оттенок соответствующего цвета мы получим. Регулируя уровни напряжения на трех аналоговых линиях в различных комбинациях, можно получить различные оттенки цветов. Например, подав одинаковые уровни на входы управления красным и зеленым цветом (при нулевом синем) можно получить желтый цвет. 
&lt;/p&gt;

&lt;p&gt;
Информация на экране прорисовывается построчно сверху вниз. Каждая строка прорисовывается слева направо. Когда вся строка прорисована, мы подаем на монитор горизонтальный синхроимпульс, который сообщает монитору о том, что следующую порцию информации нужно выводить со следующей строки. Когда все строки прорисованы, мы генерируем вертикальный синхроимпульс, который сообщает монитору, что кадр закончился и далее последует информация для формирования следующего кадра.
&lt;/p&gt;

&lt;p&gt;
Для того чтобы человеческим глазом не было видно мерцания, частота обновления кадров выбирается не менее 60 Гц. Т.е. за секунду должно быть прорисовано 60 кадров. Каждый кадр в стандартном VGA-режиме состоит из 525 строк, из которых 480 являются информативными (содержащими информацию для вывода на экран), а остальные составляют передний и задний фронты синхронизации, а также нижний и верхний бордюр и не несут в себе информации о цвете. Каждая строка также логически разбита на несколько участков: передний и задний фронты синхронизации, левый и правый бордюры, видеоинформация для вывода на экран и гасящий синхроимпульс.
&lt;/p&gt;

&lt;p&gt;
Нетрудно подсчитать, что время одной строки = 1с/60 кадров / 525 строк = 31.75 мкс
&lt;/p&gt;

&lt;p&gt;
Ниже приведен рисунок, поясняющий порядок передачи информации на VGA-монитор:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/vga_sync.png?id=osa%3Aarticles%3Avga_terminal&quot; class=&quot;media&quot; title=&quot;osa:articles:vga_sync.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/vga_sync.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Немного теории&quot; [2518-6061] --&gt;
&lt;h3&gt;&lt;a name=&quot;генерация_изображения&quot; id=&quot;генерация_изображения&quot;&gt;Генерация изображения&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;генерация_синхроимпульсов&quot; id=&quot;генерация_синхроимпульсов&quot;&gt;Генерация синхроимпульсов&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Здесь все просто. Все, что нам нужно, - это генерировать импульсы отрицательной полярности длительностью 3.9 мкс с постоянным периодом в 31.75 мкс. Это будут импульсы горизонтальной синхронизации. Во время каждых 524-го и 525-го импульсов будем формировать отрицательный импульс вертикальной синхронизации. Очевидно, что для этой цели удобно воспользоваться прерыванием по таймеру. 
&lt;/p&gt;

&lt;p&gt;
Учитывая требования к синхронности самих синхроимпульсов и сигналов формирования цвета, на этапе проектирования было принято решение весь код, формирующий непосредственно изображение, поместить в прерывание. Во-первых, это позволит жестко, с точностью до такта, привязать RGB-сигналы к синхроимпульсам, а во-вторых, мы, таким образом, распараллелим процессы формирования VGA-сигналов с формированием видеопамяти и работе SPI.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;формирование_цветного_изображения&quot; id=&quot;формирование_цветного_изображения&quot;&gt;Формирование цветного изображения&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Как мы будем формировать сигналы RGB? Вариантов несколько, но, учитывая специфику выводимой информации, т.е. цветной текст, состоящий из символов шириной по 8 пикселей, где каждый символ может быть прорисован только одним цветом, было принято решение организовать вывод следующим образом (есть еще одна причина, но о ней чуть ниже): одним выводом генерируем значение пикселя &amp;quot;зажжен&amp;quot;/&amp;quot;погашен&amp;quot;, тремя выводами с открытым стоком модулируем пиксель для получения нужного цвета и одним выводом регулируем яркость. Схематически это можно изобразить так:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/vga_color_control_scheme.png?id=osa%3Aarticles%3Avga_terminal&quot; class=&quot;media&quot; title=&quot;osa:articles:vga_color_control_scheme.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/vga_color_control_scheme.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Когда на выходе Data уровень &amp;quot;0&amp;quot;, вне зависимости от состояний остальных выходов все линии RGB будут в &amp;quot;0&amp;quot;, что будет соответствовать черному цвету. Но когда на выходе Data уровень &amp;quot;1&amp;quot;, то состояние линий RGB будет выбираться сигналами контроллера ~R, ~G и ~B. Если вывод установлен в &amp;quot;0&amp;quot; и соответствующий транзистор закрыт, то &amp;quot;1&amp;quot; с выхода Data будет проходить на управление цветом VGA. Если же вывод установлен в &amp;quot;1&amp;quot; и соответствующий транзистор открыт, то на соответствующей линии управления цветом будет &amp;quot;0&amp;quot;. Таким образом, генерируя нули и единицы на выходе Data, мы можем каждой светящейся точке задавать цвет, устанавливая выводы ~R, ~G, ~B в нужное значение. Например, для получения точки голубого цвета нам нужно убрать красную составляющую, оставив синюю и зеленую. Т.е. устанавливаем выход ~R в &amp;quot;1&amp;quot;, а ~G и ~B - в &amp;quot;0&amp;quot;. 
&lt;/p&gt;

&lt;p&gt;
У нас есть еще один выход - ~Y. Это управление яркостью. Когда он установлен в &amp;quot;0&amp;quot;, транзистор закрыт, и уровни напряжений на линиях VGA-R, VGA-G и VGA-B будут сформированы резистивным делителем, верхнее плечо которого - резистор в стоке транзисторов управления цветом, а нижнее плечо - входное сопротивление VGA = 75 Ом. Но когда выход ~Y устанавливается в &amp;quot;1&amp;quot;, все три линии управления цветом прижимаются к земле через диоды, понижая тем самым напряжение на активных линиях до 0.35В (используются диоды 1N5819).
&lt;/p&gt;

&lt;p&gt;
Таким образом, мы получаем 16 цветов: 8 обычных + 8 пониженной яркости. (На самом деле мы получаем 15 цветов, т.к. при данном подходе получаются два черных цвета.)
&lt;/p&gt;

&lt;p&gt;
На практике транзисторы с открытым стоком мы можем реализовать на самом контроллере, установив защелку выходов в &amp;quot;0&amp;quot; и управляя регистром направления выводов (TRIS). Поэтому в устройстве для задания цвета будут присутствовать только диоды и резисторы.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;формирование_последовательности_пикселей&quot; id=&quot;формирование_последовательности_пикселей&quot;&gt;Формирование последовательности пикселей&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Как видно из рисунка, поясняющего порядок синхронизации, видимая часть строки длится 25.17 мкс. Это означает, что, имея контроллер с производительностью 10 MIPS (на момент написания программы это была максимальная производительность для PIC18), т.е. со временем программного цикла = 100 нс, мы можем сформировать видимую область с максимальным разрешением 251 пиксель по горизонтали. Но тут можно возразить, что потребуется как минимум один такт на формирование данных и один такт на вывод, т.е. уже 200 нс. Но мы прибегнем к возможностям периферии, а именно - воспользуемся модулем USART, который сможет сгенерировать нам последовательность из 8-ми нулей и единиц длительностью по 100 нс каждый (для этого нужно будет запустить USART в синхронном режиме на максимальной скорости). Таким образом, нам понадобятся всего &lt;span class=&quot;important&quot;&gt;два такта&lt;/span&gt; для формирования &lt;span class=&quot;important&quot;&gt;8 пикселей&lt;/span&gt;. И еще 6 тактов остаются на всякие дополнительные нужды: чтение таблицы знакогенератора и формирование цвета. 
&lt;/p&gt;

&lt;p&gt;
(Использование USART является второй причиной, по которой мы для формирования последовательности &amp;quot;зажженных&amp;quot;/&amp;quot;погашенных&amp;quot; пикселей используем один вывод контроллера).
&lt;/p&gt;

&lt;p&gt;
Т.к. предполагается использовать шрифт шириной 8 пикселей, то и разрешение у нас будет кратным 8, т.е. максимально возможное - 248 точек (31 символ). Однако для простоты и удобства возьмем 30. Таким образом, разрешение видеоизображения по горизонтали будет 240 точек. Для вывода изображения нам нужно после генерации синхроимпульса выдержать паузу (левый бордюр), затем выполнить прорисовку текущей линии изображения, затем выдержать еще паузу (правый бордюр). Порядок обработки прерывания будет таким:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/vga_interrupt.png?id=osa%3Aarticles%3Avga_terminal&quot; class=&quot;media&quot; title=&quot;osa:articles:vga_interrupt.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/vga_interrupt.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
При такой организации у нас от каждой строки остается 3.9 мкс (39 инструкций, за вычетом 5-6 на вход/выход в прерывание и сброс бита прерывания) на обработку данных от SPI. Паузы здесь отмечены условно, т.е. на самом деле это время используется на сопутствующие вычисления (эмуляцию курсора, подсчет строк, формирование указателей). 
&lt;/p&gt;

&lt;p&gt;
В нашей программе применяется шрифт шириной 8 пикселей. Знакогенератор хранится во внутренней ROM контроллера и представляет собой массив, в котором на описание каждого символа выделяется 16 байт (по одному байту на каждую строку растра). Например, буква &amp;#039;A&amp;#039; записана так:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/vga_trm_symbol.png?id=osa%3Aarticles%3Avga_terminal&quot; class=&quot;media&quot; title=&quot;osa:articles:vga_trm_symbol.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/vga_trm_symbol.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
И для вывода этого символа мы будем в течение прорисовки 16 линий через один и тот же промежуток времени после начала горизонтального синхроимпульса записывать в регистр TXREG все байты, описывающие этот символ: в первой и второй линиях байты 0х00, в третьей - 0x10, в четвертой - 0x38 и т.д. Тогда на экране это будет выглядеть так:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/vga_trm_symbol_on_screen.png?id=osa%3Aarticles%3Avga_terminal&quot; class=&quot;media&quot; title=&quot;osa:articles:vga_trm_symbol_on_screen.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/vga_trm_symbol_on_screen.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Здесь ts - момент начала строки, tc - момент старта символа.
&lt;/p&gt;

&lt;p&gt;
Для вывода одной строки растра каждого символа у нас есть 8 тактов. За это время мы должны успеть выбрать текущий байт из массива знакогенератора для следующего символа и подготовить значение цвета для него. Ровно через 8 тактов после начала вывода предыдущего символа мы выводим следующий, еще через 8 - очередной и так выводятся все 30 символов.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Генерация изображения&quot; [6062-17409] --&gt;
&lt;h2&gt;&lt;a name=&quot;принципиальная_схема&quot; id=&quot;принципиальная_схема&quot;&gt;Принципиальная схема&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/vga_terminal_scheme_.png?id=osa%3Aarticles%3Avga_terminal&quot; class=&quot;media&quot; title=&quot;osa:articles:vga_terminal_scheme_.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/vga_terminal_scheme_.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Два слова о некоторых схемных решениях.
&lt;/p&gt;

&lt;p&gt;
В первую очередь нужно обратить внимание на необязательную, но желательную микросхему 74AC245. Ее основная функция - разгрузить вывод RC7. Т.к. входы линий цвета VGA являются низкоомными (75 Ом), то ток, которым они управляются, получается сравнительно велик. Выводу RC7 приходится работать на низкоомную нагрузку сразу по трем линиям: управление красным, зеленым и синим. В принципе, эту микросхему можно и не ставить, а нагрузить все целиком на RC7, но при этом картинка на экране получится немного тусклой.
&lt;/p&gt;

&lt;p&gt;
Во вторую очередь обратим внимание на RC-цепочку, стоящую на выходе RC7 (R4-C8). При отладке программы было обнаружено несоответствие поведения контроллера PIC18F252 (такое же несоответствие было установлено для PIC18F258, PIC18F452 и PIC18F458) с описанием в документации: там сказано, что импульсы USART, работающего в синхронном режиме на максимальной скорости, синхронизированы с тактом Q4. Однако на практике выяснилось, что они синхронизированы с тактом Q2. Для задания цвета каждому символу регистр цвета (TRISB) обновлялся одновременно с началом передачи по USART очередного байта. Проблема в том, что запись в регистр TRISB в этих контроллерах производится как раз по такту Q4. Т.е. запись в регистр управления цветом рассинхронизирована с началом передачи очередного байта по USART на Q4-Q2 = 50 нс (при тактовой частоте 40 МГц). На экране эта рассинхронизация проявляется в виде &amp;quot;заползания&amp;quot; цвета соседних символов друг на друга. Для того чтобы избавиться от этого эффекта, в схему и была внедрена RC-цепочка, задерживающая сигнал с выхода RC7 на 50 нс.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Примечание&lt;/strong&gt;. &lt;em&gt;Это несоответствие было обнаружено только на PIC18F252, PIC18F258, PIC18F452 и PIC18F458. Если будет использован контроллер более новой модификации (например, 2520), то RC-цепочку ставить не нужно.&lt;/em&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Принципиальная схема&quot; [17410-20693] --&gt;
&lt;h2&gt;&lt;a name=&quot;организация_программы&quot; id=&quot;организация_программы&quot;&gt;Организация программы&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Организация программы&quot; [20694-20747] --&gt;
&lt;h3&gt;&lt;a name=&quot;структура_видеопамяти&quot; id=&quot;структура_видеопамяти&quot;&gt;Структура видеопамяти&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Видеопамять содержит следующую информацию:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; массив символов, выводимых на экран (8 бит на символ);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; массив цветов для всех знакомест (4 бита на символ).&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Экранное поле разбито на 900 знакомест (30 по вертикали и 30 по горизонтали). Таким образом, требуется 900 байт для хранения символов и 450 байт для хранения массива цветов. Учитывая требования по минимизации времени обработки данных в этих двух массивах, было решено организовать видеопамять в виде массива 3-х байтовых блоков: 
&lt;/p&gt;
&lt;table class=&quot;inline&quot;&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;th class=&quot;col0&quot;&gt;xy&lt;/th&gt;&lt;th class=&quot;col1&quot;&gt;XX&lt;/th&gt;&lt;th class=&quot;col2&quot;&gt;YY&lt;/th&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;

где:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; x - 4 бита, задающие цвет символа XX;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; y - 4 бита, задающие цвет символа YY;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; XX - символ в четной позиции знакоместа;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; YY - символ в нечетной позиции знакоместа.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Эти трехбайтовые блоки хранятся в памяти последовательно в виде массива из 450 блоков. Такая организация памяти позволяет адресовать сразу два массива (массив символов и массив цветов) одним и тем же указателем FSR.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Структура видеопамяти&quot; [20748-22363] --&gt;
&lt;h3&gt;&lt;a name=&quot;распределение_sfr&quot; id=&quot;распределение_sfr&quot;&gt;Распределение SFR&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Для того чтобы не тратить дефицитное время на формирование FSR-регистров для указания на активный символ (для отображения или для записи), а также еще некоторых специальных регистров, программа организована таким образом, что за некоторыми регистрами закреплены определенные функции на протяжении всего времени выполнения программы:
&lt;/p&gt;
&lt;table class = &quot;fpl&quot;&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;strong&gt;FSR1&lt;/strong&gt;		&lt;/td&gt;
		&lt;td&gt;
всегда указывает на активный блок в массиве видеопамяти, куда производится запись (фактически - текущая позиция курсора).		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;strong&gt;FSR2&lt;/strong&gt;		&lt;/td&gt;
		&lt;td&gt;
Всегда указывает на текущий выводимый на экран символ		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;strong&gt;TBLPTR&lt;/strong&gt;		&lt;/td&gt;
		&lt;td&gt;
Всегда указывает на массив знакогенератора (на текущую линию; подробнее см. &lt;a href=&quot;#знакогенератор&quot; title=&quot;osa:articles:vga_terminal &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;Знакогенератор&lt;/a&gt;)		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;
&lt;strong&gt;PCLATH&lt;/strong&gt;		&lt;/td&gt;
		&lt;td&gt;
Всегда содержит значение 0х4 - старший байт адреса подпрограммы обработки SPI (т.к. там присутствует таблица переходов, обращение к которой реализовано присвоением регистру PCL)		&lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;

Такое закрепление регистров позволит сэкономить время, которое могло быть затрачено на:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; формирование указателей (и RAM и ROM);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; сохранение/восстановление SFR при входе/выходе в/из прерывания.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Распределение SFR&quot; [22364-24262] --&gt;
&lt;h3&gt;&lt;a name=&quot;знакогенератор&quot; id=&quot;знакогенератор&quot;&gt;Знакогенератор&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Знакогенератор организован в виде массива байтов для хранения информации о 256-ти символах по 16 байт на символ. Эти данные нам нужно разместить в определенном порядке, чтобы максимально сократить время выборки требуемого для вывода на экран байта с учетом того, что за один проход (за прорисовку одной строки растра) из всех 16-байт, описывающих конкретный символ, для каждого символа будет выбран только один байт, причем с одним и тем же индексом. Т.е., выводя строку номер 5, нам потребуются только 5-е байты из 16-ти, описывающих каждый символ. Поэтому массив знакогенератора организован не в виде массива 16-байтов блоков, каждый их которых описывает один символ, а в виде массива из 16-ти 256-байтовых блоков, каждый из которых содержит на каждый символ по одному байту, который будет выводиться  при прорисовке текущей линии.
&lt;/p&gt;

&lt;p&gt;
Такая организация позволит при прорисовке очередной строки растра один раз сформировать значение регистра TBLPTRH, который будет указывать на 256-байтный блок, и для каждого символа просто прописывать его значение в регистр TBLPTRL, получая тем самым адрес байта для вывода на экран. С учетом такого подхода у нас есть одно требование: таблица знакогенератора должна быть выровнена по границе 256 байт. В нашей программе знакогенератор будет храниться по адресу 0x1000.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Знакогенератор&quot; [24263-26640] --&gt;
&lt;h3&gt;&lt;a name=&quot;таблица_символов&quot; id=&quot;таблица_символов&quot;&gt;Таблица символов&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/vga_trm_fonttable.png?id=osa%3Aarticles%3Avga_terminal&quot; class=&quot;media&quot; title=&quot;osa:articles:vga_trm_fonttable.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/vga_trm_fonttable.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
(Символы с кодами с 0x80 по 0x98, выстроенные в 5 рядов по 5 символов, образуют графическое изображение логотипа Microchip.)
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Таблица символов&quot; [26641-26933] --&gt;
&lt;h3&gt;&lt;a name=&quot;курсор&quot; id=&quot;курсор&quot;&gt;Курсор&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Терминал способен отображать мигающий курсор в текущей позиции. Это может оказаться полезным для более удобной организации ввода данных пользователем. Скорость мигания курсора определяется периодом таймера TMR0 (курсор мигает с частотой 2.5 Гц). Вывод курсора в программе организован следующим образом: при прорисовке той строки символов, которая содержит курсор, 2.5 раза в секунду символ, в позиции которого находится курсор, подменяется символом с кодом 0x00 (он представляет собой закрашенный символ), а после прорисовки строки - восстанавливается. 
&lt;/p&gt;

&lt;p&gt;
Размер курсора может изменяться пользователем. Чем больше размер курсора, тем в большем количестве строк растра, соответствующих строке, содержащей курсор, будет произведена такая замена.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Курсор&quot; [26934-28311] --&gt;
&lt;h3&gt;&lt;a name=&quot;демонстрация&quot; id=&quot;демонстрация&quot;&gt;Демонстрация&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Если при подаче питания удерживать вход RA0 в &amp;quot;0&amp;quot;, то программа войдет в режим демонстрации, в котором будет отображаться тестовая картинка (см. выше). Как только на RA0 появляется &amp;quot;1&amp;quot;, экран очищается, и программа начинает работать в обычном режиме.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Демонстрация&quot; [28312-28791] --&gt;
&lt;h2&gt;&lt;a name=&quot;управление_терминалом&quot; id=&quot;управление_терминалом&quot;&gt;Управление терминалом&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Управление терминалом&quot; [28792-28847] --&gt;
&lt;h3&gt;&lt;a name=&quot;интерфейс_spi&quot; id=&quot;интерфейс_spi&quot;&gt;Интерфейс SPI&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Наш терминал управляется внешним устройством по SPI по четырем линиям: notSS, SDI, SDO, SCK. При передачи на терминал (или при приеме от терминала) каждого байта линия notSS должна быть установлена в 0, а по завершении передачи (приема) байта - возвращена в единичное состояние. Данные (входные и выходные) синхронизируются по отрицательному фронту линии SCK. Ввиду того, что большую часть времени программа занята прорисовкой изображения, и из 31.5 мкс остается только около 2.5 мкс, т.е. 7.5% времени. Это накладывает некоторые ограничения на скорость обмена данными с терминалом. Программу удалось организовать так, что за 2.5 мкс будет успевать полностью обработаться один байт данных, т.е. за время прорисовки одной строки растра терминал может принять и полностью обработать один байт. Таким образом, частота следования байтов не может превышать частоты прорисовки строк растра, т.е. 1/31.5 мкс ~ 31.5 Кбайт/сек. 
&lt;p&gt;&lt;div class=&quot;noteclassic&quot;&gt;
Т.е. частота SPI не должна превышать &lt;strong&gt;250 кбит/сек&lt;/strong&gt;.

&lt;/div&gt;&lt;/p&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Интерфейс SPI&quot; [28848-30607] --&gt;
&lt;h3&gt;&lt;a name=&quot;вывод_данных_на_экран&quot; id=&quot;вывод_данных_на_экран&quot;&gt;Вывод данных на экран&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Для вывода символа на экран в текущей позиции курсора нужно просто передать терминалу по SPI его код от 0x10 до 0xFF. Байты с 0x00 до 0x0F зарезервированы для команд (см. &lt;a href=&quot;#команды_управления&quot; title=&quot;osa:articles:vga_terminal &amp;crarr;&quot; class=&quot;wikilink1&quot;&gt;команды управления&lt;/a&gt;). При этом:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; в текущей позиции курсора будет выведен полученный символ;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; этому символу будет установлен текущий цвет;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; текущая позиция увеличивается на 1. Если она была в конце строки, то производится перевод на начало следующей. Если позиция находилась в конце экрана (нижний правый угол), то новая позиция курсора будет установлена в начало экрана (верхний левый угол).&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Вывод данных на экран&quot; [30608-31671] --&gt;
&lt;h3&gt;&lt;a name=&quot;команды_управления&quot; id=&quot;команды_управления&quot;&gt;Команды управления&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Байты 0x00-0x0F зарезервированы для передачи терминалу управляющих команд (установка позиции курсора, текущего цвета, размера курсора и пр.)

&lt;/p&gt;
&lt;table class=&quot;inline&quot;&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;th class=&quot;col0 centeralign&quot;&gt;  Код   &lt;/th&gt;&lt;th class=&quot;col1 centeralign&quot;&gt;  Команда     &lt;/th&gt;&lt;th class=&quot;col2 centeralign&quot;&gt;  Параметры  &lt;/th&gt;&lt;th class=&quot;col3 centeralign&quot;&gt;  Описание  &lt;/th&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row1&quot;&gt;
		&lt;td class=&quot;col0 centeralign&quot;&gt;  00  &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; SPI_CMD_NOP    &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; -          &lt;/td&gt;&lt;td class=&quot;col3 leftalign&quot;&gt; Команда ничего не делает  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row2&quot;&gt;
		&lt;td class=&quot;col0 centeralign&quot;&gt;  01  &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; SPI_CMD_SET_X  &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; X          &lt;/td&gt;&lt;td class=&quot;col3 leftalign&quot;&gt; Установить позицию курсора по горизонтали (0..X_SIZE-1)(пр.1)  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row3&quot;&gt;
		&lt;td class=&quot;col0 centeralign&quot;&gt;  02  &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; SPI_CMD_SET_Y  &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; Y          &lt;/td&gt;&lt;td class=&quot;col3 leftalign&quot;&gt; Установить позицию курсора по вертикали (0..Y_SIZE-1)(пр.1)   &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row4&quot;&gt;
		&lt;td class=&quot;col0 centeralign&quot;&gt;  03  &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; SPI_CMD_SET_CURSOR  &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; S     &lt;/td&gt;&lt;td class=&quot;col3 leftalign&quot;&gt; Выбор размера курсора (0..15)  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row5&quot;&gt;
		&lt;td class=&quot;col0 centeralign&quot;&gt;  04  &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; SPI_CMD_SET_COLOR   &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; C     &lt;/td&gt;&lt;td class=&quot;col3 leftalign&quot;&gt; Установка текущего цвета (0..15)  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row6&quot;&gt;
		&lt;td class=&quot;col0 centeralign&quot;&gt;  05  &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; SPI_CMD_SET_SYMBOL_COLOR  &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; C  &lt;/td&gt;&lt;td class=&quot;col3 leftalign&quot;&gt; Установка цвета символа, находящегося в текущей позиции курсора (0..15). Текущий цвет при этом не меняется.  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row7&quot;&gt;
		&lt;td class=&quot;col0 centeralign&quot;&gt;  06  &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; SPI_CMD_CLRSCR  &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; -         &lt;/td&gt;&lt;td class=&quot;col3 leftalign&quot;&gt; Очистить экран, установить текущий цвет = 15, установить курсор в позицию 0,0. Выполнение данной команды требует длительного времени (около 2.5 мс). В течение этого времени терминал не будет обрабатывать никаких других команд, кроме команды SPI_CMD_CHECK. Есть два пути определить, что выполнение команды завершено: выдержка паузы 2.5 мс или запрос состояния выполнения этой команды командой SPI_CMD_CHECK до тех пор, пока не будет получен ответ 0x55  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row8&quot;&gt;
		&lt;td class=&quot;col0 centeralign&quot;&gt;  07  &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; SPI_CMD_CHECK   &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; state (пр.2)        &lt;/td&gt;&lt;td class=&quot;col3 leftalign&quot;&gt; Команда проверки состояния выполнения команды очистки экрана. Возвращает 0x55, если очистка экрана завершена (или не производится в данный момент)  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row9&quot;&gt;
		&lt;td class=&quot;col0 centeralign&quot;&gt;  08  &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; SPI_CMD_GET_SYMBOL  &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; char (пр.2)     &lt;/td&gt;&lt;td class=&quot;col3 leftalign&quot;&gt; Прочитать символ из видеопамяти в текущей позиции курсора  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row10&quot;&gt;
		&lt;td class=&quot;col0 centeralign&quot;&gt;  09  &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; SPI_CMD_GET_X       &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; X (пр.2)        &lt;/td&gt;&lt;td class=&quot;col3 leftalign&quot;&gt; Прочитать текущую позицию курсора по горизонтали  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row11&quot;&gt;
		&lt;td class=&quot;col0 centeralign&quot;&gt;  0A  &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; SPI_CMD_GET_Y       &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; Y (пр.2)        &lt;/td&gt;&lt;td class=&quot;col3 leftalign&quot;&gt; Прочитать текущую позицию курсора по вертикали  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row12&quot;&gt;
		&lt;td class=&quot;col0 centeralign&quot;&gt;  0B  &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; SPI_CMD_GET_CURSOR  &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; S (пр.2)        &lt;/td&gt;&lt;td class=&quot;col3 leftalign&quot;&gt; Прочитать текущий размер курсора  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row13&quot;&gt;
		&lt;td class=&quot;col0 centeralign&quot;&gt;  0C  &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; SPI_CMD_GET_COLOR   &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; C (пр.2)        &lt;/td&gt;&lt;td class=&quot;col3 leftalign&quot;&gt; Прочитать текущий цвет  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row14&quot;&gt;
		&lt;td class=&quot;col0 centeralign&quot;&gt;  0D  &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; SPI_CMD_GET_SYMBOL_COLOR  &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; C (пр.2)  &lt;/td&gt;&lt;td class=&quot;col3 leftalign&quot;&gt; Прочитать значение цвета символа в текущей позиции курсора  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row15&quot;&gt;
		&lt;td class=&quot;col0 centeralign&quot;&gt;  0E  &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; SPI_CMD_GET_X_SIZE  &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; X_SIZE (пр.2)   &lt;/td&gt;&lt;td class=&quot;col3 leftalign&quot;&gt; Получить размер экрана по горизонтали (пр.3)  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row16&quot;&gt;
		&lt;td class=&quot;col0 centeralign&quot;&gt;  0F  &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; SPI_CMD_GET_Y_SIZE  &lt;/td&gt;&lt;td class=&quot;col2 leftalign&quot;&gt; Y_SIZE (пр.2)   &lt;/td&gt;&lt;td class=&quot;col3 leftalign&quot;&gt; Получить размер экрана по вертикали (пр.3)  &lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;

&lt;em&gt;&lt;strong&gt;Примечания&lt;/strong&gt;&lt;/em&gt;:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Значения констант X_SIZE и Y_SIZE можно получить командами 0x0E и 0x0F, соответственно;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Чтение любого параметра из терминала производится в три этапа: &lt;/div&gt;
&lt;ol&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; на терминал отправляется команда (см. таблицу);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; выдерживается пауза не менее 32 мкс;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; производится чтение байта.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Эти команды добавлены для того, чтобы осталась возможность расширения возможностей программы терминала. Например, при переносе программы на более мощный контроллер (PIC24, PIC32, ATMEGA и т.д.) появится возможность увеличить разрешение и, следовательно, количество символов в строке. Или кто-нибудь решит переделать программу так, чтобы размер символа был не 8x16, а 8x8, увеличив тем самым количество текстовых строк.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Команды управления&quot; [31672-36350] --&gt;
&lt;h2&gt;&lt;a name=&quot;исходные_тексты&quot; id=&quot;исходные_тексты&quot;&gt;Исходные тексты&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Исходные тексты программы терминала можно взять здесь: &lt;a href=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/terminal.rar&quot; class=&quot;media mediafile mf_rar&quot; title=&quot;osa:articles:terminal.rar&quot;&gt;terminal.rar&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Исходные тексты&quot; [36351-36528] --&gt;
&lt;h3&gt;&lt;a name=&quot;библиотека&quot; id=&quot;библиотека&quot;&gt;Библиотека&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Для удобства разработки собственных приложений, управляющих терминалом, создан файл библиотеки. Он расположен в папке APPNOTE. Файл &amp;quot;vga_terminal.c&amp;quot; добавляется в проект, а файл &amp;quot;vga_terminal.h&amp;quot; включается во все файлы, из которых предполагается делать вызов функций терминала, директивой #include.
&lt;/p&gt;

&lt;p&gt;
Ниже приведен список Си-фукций модуля vga_terminal.

&lt;/p&gt;
&lt;table class=&quot;inline&quot;&gt;
	&lt;tr class=&quot;row0&quot;&gt;
		&lt;th class=&quot;col0 centeralign&quot;&gt;  Функция   &lt;/th&gt;&lt;th class=&quot;col1 centeralign&quot;&gt;  Описание  &lt;/th&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row1&quot;&gt;
		&lt;td class=&quot;col0 centeralign&quot; colspan=&quot;2&quot;&gt;  &lt;span class=&quot;important&quot;&gt;Вывод данных&lt;/span&gt;  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row2&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; void &lt;strong&gt;vga_outchar&lt;/strong&gt; (char c);                          &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; Вывести символ в текущей позиции курсора  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row3&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; void &lt;strong&gt;vga_outcharxy&lt;/strong&gt; (char x, char y, char c);        &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; Вывести символ в указанной позиции курсора.  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row4&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; void &lt;strong&gt;vga_outtext&lt;/strong&gt; (const char *t);                   &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; Вывести текст (строка, заканчивающаяся нулевым символом, расположенная в ROM) в текущей позиции курсора  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row5&quot;&gt;
		&lt;td class=&quot;col0&quot;&gt; void &lt;strong&gt;vga_outtextxy&lt;/strong&gt; (char x, char y, const char *t); &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; Вывести текст (строка, заканчивающаяся нулевым символом, расположенная в ROM) в указанной позиции.  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row6&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; void &lt;strong&gt;vga_outstr&lt;/strong&gt; (char *s);                          &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; Вывести текст (строка, заканчивающаяся нулевым символом, расположенная в RAM) в текущей позиции курсора  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row7&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; void &lt;strong&gt;vga_outstrxy&lt;/strong&gt; (char x, char y, char *s);        &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; Вывести текст (строка, заканчивающаяся нулевым символом, расположенная в RAM) в указанной позиции  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row8&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; void &lt;strong&gt;vga_window&lt;/strong&gt; (char x1, char y1, char x2, char y2, char color);  &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Нарисовать пустое прямоугольное окно в указанных координатах с рамкой указанного цвета. &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row9&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; char &lt;strong&gt;vga_getchar&lt;/strong&gt; (void);                            &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; Прочитать символ в текущей позиции курсора  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row10&quot;&gt;
		&lt;td class=&quot;col0 centeralign&quot; colspan=&quot;2&quot;&gt;  &lt;span class=&quot;important&quot;&gt;Очистка экрана&lt;/span&gt;  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row11&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; void &lt;strong&gt;vga_clrscr&lt;/strong&gt; (void);                             &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; Начать очистку экрана. После этой функции нужно либо выдержать паузу 2.5 мс, либо проверять окончание ее выполнения функцией vga_check() (пока она не вернет &amp;quot;1&amp;quot;)  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row12&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; char &lt;strong&gt;vga_check&lt;/strong&gt; (void);                              &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; Возвращает &amp;quot;1&amp;quot;, если терминал готов принимать данные. Иначе возвращает &amp;quot;0&amp;quot;. Применяется в основном для проверки окончания выполнения очистки экрана.  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row13&quot;&gt;
		&lt;td class=&quot;col0 centeralign&quot; colspan=&quot;2&quot;&gt;  &lt;span class=&quot;important&quot;&gt;Управление цветом&lt;/span&gt;  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row14&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; void &lt;strong&gt;vga_setcolor&lt;/strong&gt; (char color);                     &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; Установить текущий цвет.  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row15&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; char &lt;strong&gt;vga_getcolor&lt;/strong&gt; (void);                           &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; Прочитать текущий цвет.  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row16&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; char &lt;strong&gt;vga_getcharcolor&lt;/strong&gt; (void);                       &lt;/td&gt;&lt;td class=&quot;col1&quot;&gt; Прочитать цвет символа расположенного в текущей позиции курсора &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row17&quot;&gt;
		&lt;td class=&quot;col0 centeralign&quot; colspan=&quot;2&quot;&gt;  &lt;span class=&quot;important&quot;&gt;Управление курсором&lt;/span&gt;  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row18&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; void &lt;strong&gt;vga_gotoxy&lt;/strong&gt; (char x, char y);                   &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; Переместить курсор в указанное место  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row19&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; void &lt;strong&gt;vga_setcursorsize&lt;/strong&gt; (char size);                 &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; Установить размер курсора (0 - спрятать)  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row20&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; char &lt;strong&gt;vga_getx&lt;/strong&gt; (void);                               &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; Получить текущую X-позицию курсора  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row21&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; char &lt;strong&gt;vga_gety&lt;/strong&gt; (void);                               &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; Получить текущую Y-позицию курсора  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row22&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; char &lt;strong&gt;vga_getwidth&lt;/strong&gt; (void);                           &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; Получить ширину экрана (в символах)  &lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr class=&quot;row23&quot;&gt;
		&lt;td class=&quot;col0 leftalign&quot;&gt; char &lt;strong&gt;vga_getheight&lt;/strong&gt; (void);                          &lt;/td&gt;&lt;td class=&quot;col1 leftalign&quot;&gt; Получить высоту экрана (в символах)  &lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Библиотека&quot; [36529-40865] --&gt;
&lt;h3&gt;&lt;a name=&quot;тест-приложение&quot; id=&quot;тест-приложение&quot;&gt;Тест-приложение&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Маленькая программа для PIC16F88, демонстрирующая использование библиотеки vga_terminal для управления терминалом: &lt;a href=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/terminal_test.rar&quot; class=&quot;media mediafile mf_rar&quot; title=&quot;osa:articles:terminal_test.rar&quot;&gt;terminal_test.rar&lt;/a&gt;
Для связи используется аппаратный SPI.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/vga_test_terminal_scheme.png?id=osa%3Aarticles%3Avga_terminal&quot; class=&quot;media&quot; title=&quot;osa:articles:vga_test_terminal_scheme.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/vga_test_terminal_scheme.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Программа выводит на экран следующую картинку:
&lt;/p&gt;

&lt;p&gt;

&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/vga_trm_test_screen.jpg?id=osa%3Aarticles%3Avga_terminal&quot; class=&quot;media&quot; title=&quot;osa:articles:vga_trm_test_screen.jpg&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/vga_trm_test_screen.jpg?w=400&quot; class=&quot;media&quot; alt=&quot;&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Тест-приложение&quot; [40866-41397] --&gt;
&lt;h2&gt;&lt;a name=&quot;поделки_читателей&quot; id=&quot;поделки_читателей&quot;&gt;Поделки читателей&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

&lt;a href=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/vga_3051.zip&quot; class=&quot;media mediafile mf_zip&quot; title=&quot;osa:articles:vga_3051.zip&quot;&gt;Терминал с разрешением 51x30 символов&lt;/a&gt; - читатель &lt;a href=&quot;mailto:&amp;#x69;&amp;#x67;&amp;#x6f;&amp;#x72;&amp;#x6b;&amp;#x6f;&amp;#x76;&amp;#x36;&amp;#x40;&amp;#x67;&amp;#x6d;&amp;#x61;&amp;#x69;&amp;#x6c;&amp;#x2e;&amp;#x63;&amp;#x6f;&amp;#x6d;&quot; class=&quot;mail JSnocheck&quot; title=&quot;&amp;#x69;&amp;#x67;&amp;#x6f;&amp;#x72;&amp;#x6b;&amp;#x6f;&amp;#x76;&amp;#x36;&amp;#x40;&amp;#x67;&amp;#x6d;&amp;#x61;&amp;#x69;&amp;#x6c;&amp;#x2e;&amp;#x63;&amp;#x6f;&amp;#x6d;&quot;&gt;Ковалев Игорь&lt;/a&gt; переделал терминал для работы на 16МГц (PIC18F46K20). За счет увеличения скорости он увеличил горизонтальное разрешение; символы визуально сузились, что сделало изображение более симпатичным 
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/vga3051.jpg?id=osa%3Aarticles%3Avga_terminal&quot; class=&quot;media&quot; title=&quot;osa:articles:vga3051.jpg&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/vga3051.jpg?w=200&quot; class=&quot;media&quot; title=&quot;катринка&quot; alt=&quot;катринка&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Поделки читателей&quot; [41398-42005] --&gt;
&lt;h2&gt;&lt;a name=&quot;ссылки&quot; id=&quot;ссылки&quot;&gt;Ссылки&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

&lt;a href=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/vga_pic24.zip&quot; class=&quot;media mediafile mf_zip&quot; title=&quot;osa:articles:vga_pic24.zip&quot;&gt;VGA-Терминал на PIC24&lt;/a&gt; - 60х25 символов, 8 цветов, 2 размера шрифта, PS/2 клавиатура, USART. Схема, исходники, отличное описание (англ.)
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.micro-examples.com/public/microex-navig/doc/089-pic-pal-tv.html&quot; class=&quot;urlextern&quot; title=&quot;http://www.micro-examples.com/public/microex-navig/doc/089-pic-pal-tv.html&quot;  rel=&quot;nofollow&quot;&gt;http://www.micro-examples.com/public/microex-navig/doc/089-pic-pal-tv.html&lt;/a&gt; - PIC PAL Video Library - проект для PIC18F4620, написанный на mikorC 
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.mikrocontroller.net/topic/53140&quot; class=&quot;urlextern&quot; title=&quot;http://www.mikrocontroller.net/topic/53140&quot;  rel=&quot;nofollow&quot;&gt;http://www.mikrocontroller.net/topic/53140&lt;/a&gt; - текстовый терминал на ATmega8, 40x25, управление UART 19200 (вывод PAL)
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://neil.franklin.ch/Projects/SoftVGA/&quot; class=&quot;urlextern&quot; title=&quot;http://neil.franklin.ch/Projects/SoftVGA/&quot;  rel=&quot;nofollow&quot;&gt;http://neil.franklin.ch/Projects/SoftVGA/&lt;/a&gt; - Еще один проект для atmega32.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://mondo-technology.com/dcvideo.html&quot; class=&quot;urlextern&quot; title=&quot;http://mondo-technology.com/dcvideo.html&quot;  rel=&quot;nofollow&quot;&gt;http://mondo-technology.com/dcvideo.html&lt;/a&gt; - текстовый терминал на PIC16F819 с видео выходом. Разрешение 20x10 символов, управление: UART 9600
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Ссылки&quot; [42006-] --&gt;</description>
    </item>
    <item rdf:about="http://www.pic24.ru/doku.php/osa/articles/volatile_for_chainiks?rev=1675385653">
        <dc:format>text/html</dc:format>
        <dc:date>2023-02-03T03:54:13+03:00</dc:date>
        <title>volatile для &quot;чайников&quot;</title>
        <link>http://www.pic24.ru/doku.php/osa/articles/volatile_for_chainiks?rev=1675385653</link>
        <description>


&lt;h1&gt;&lt;a name=&quot;volatile_для_чайников&quot; id=&quot;volatile_для_чайников&quot;&gt;volatile для &amp;quot;чайников&amp;quot;&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;

&lt;a href=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/volatile_for_chainiks.pdf&quot; class=&quot;media mediafile mf_pdf&quot; title=&quot;osa:articles:volatile_for_chainiks.pdf&quot;&gt;Скачать в PDF-формате&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Виктор Тимофеев, июнь, 2010
&lt;a href=&quot;mailto:&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot; class=&quot;mail JSnocheck&quot; title=&quot;&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot;&gt;osa@pic24.ru&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;volatile для чайников&quot; [1-199] --&gt;
&lt;h2&gt;&lt;a name=&quot;вступление&quot; id=&quot;вступление&quot;&gt;Вступление&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Данная статья предназначена для программистов встраиваемых систем, в основном - для начинающих работать с языком Си, но опытные программисты, надеюсь, тоже смогут что-то для себя почерпнуть. При написании статьи я, возможно, чего-то не учел или где-то ошибся. Буду рад любым поправкам и любой критике.

&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;

&lt;br/&gt;
 
Разбирая чужие исходники, часто натыкаюсь на ошибки программистов, связанные с недопониманием назначения квалификатора &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;. Результатом такого недопонимания является код, который дает редкие, совершенно непредсказуемые и, зачастую, очень разрушительные и необратимые сбои. Это особенно актуально для микроконтроллерных систем, где, во-первых, обработчики прерываний являются частью прикладного кода, а, во-вторых, регистры управления периферией отображаются в RAM общего назначения. Ошибки, связанные с неиспользованием (или неправильным использованием) квалификатора &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; трудно поддаются отладке из-за их непредсказуемости и неповторяемости. Бывает, что Си-код, в котором не предусмотрено использование &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; там, где его надо бы использовать, прекрасно работает, будучи собранным одним компилятором, и сбоит (или не работает вовсе), когда собирается другим.
&lt;/p&gt;

&lt;p&gt;
Большинство примеров, приведенных в этой статье, написаны для компилятора GCC (Mplab C30), потому что, учитывая архитектуру ядра PIC24 и особенности компилятора, для него проще всего синтезировать маленькие наглядные примеры, в которых будет проявляться неправильное обращение с &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;. Многие из этих примеров будут собираться совершенно корректно на других (более простых) компиляторах, таких как PICC или MicroC. Но это не значит, что при работе с этими компиляторами ошибки неиспользования &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; не проявляются вовсе. Просто код демонстрации для этих компиляторов выглядел бы намного больше и сложнее. 
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Вступление&quot; [200-3583] --&gt;
&lt;h2&gt;&lt;a name=&quot;определение&quot; id=&quot;определение&quot;&gt;Определение&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

(&lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; в переводе с английского означает &amp;quot;нестабильный&amp;quot;, &amp;quot;изменчивый&amp;quot;)
&lt;/p&gt;

&lt;p&gt;
&lt;p&gt;&lt;div class=&quot;noteclassic&quot;&gt;Итак, &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; в языке Си - это квалификатор переменной, говорящий компилятору, что значение переменной может быть изменено в любой момент и что часть кода, которая производит над этой переменной какие-то действия (чтение или запись), не должна быть оптимизирована. 
&lt;/div&gt;&lt;/p&gt;
&lt;/p&gt;

&lt;p&gt;
Что это значит? Известно, что одной из характеристик компиляторов, говорящих за их качество, является способность оптимизировать генерируемый объектный код. Для этого они объединяют повторяющиеся конструкции, сохраняют в регистрах общего назначения промежуточные результаты вычислений, выстраивают последовательность команд так, чтобы минимизировать долго выполняющиеся фрагменты кода (например, обращение через косвенную адресацию), и т.д. Выполняя такую оптимизацию, они немного преобразует наш код, подменяя его идентичным с точки зрения алгоритма, но более быстрым и/или компактным. Но такую подмену можно делать не всегда. Рассмотрим пример:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;   &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; a &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
   ...
   &lt;span class=&quot;me1&quot;&gt;a&lt;/span&gt; |= &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
   a |= &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;;
   ...&lt;/pre&gt;
&lt;p&gt;
С точки зрения алгоритма устанавливаются два младших разряда в переменной a. Оптимизатор может сделать подмену такого кода одним оператором:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    a |= &lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;

выиграв таким образом пару тактов и пару ячеек ROM. Но представим себе, что эти же действия мы выполняем не над какой-то абстрактной переменной, а над периферийным регистром:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    PORTB |= &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
    PORTB |= &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Вот в этом случае оптимизация с заменой на &amp;quot;PORTB |= 3&amp;quot; нас может не устроить! Управляя напрямую состояниями выводов контроллера, нам часто бывает важна последовательность изменения сигналов. Например, мы формируем сигналы SPI, и один вывод (PORTB.0) - это данные, а другой (PORTB.1) - синхроимпульсы. В этом случае нам нельзя изменять состояния этих выводов одновременно, т.к. при этом нельзя гарантировать, что управляемая микросхема по синхроимпульсу получит правильные данные. И уж тем более, нам бы не хотелось, чтобы оптимизации подвергся код, формирующий синхроимпульс длительностью в один такт:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    PORTB |= &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;;
    PORTB &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; ~&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;;&lt;/pre&gt;
&lt;p&gt;
Такой код мог бы быть воспринят компилятором как два взаимообратных действия и первая строка могла бы не попасть в результирующий объектный код. Однако на практике мы видим, что такой оптимизации не производится. Так происходит именно потому, что переменная, на которую отображается регистр PORTB, объявлена с квалификатором &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;, например:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt;      &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; PORTB @ &lt;span class=&quot;nu12&quot;&gt;0x06&lt;/span&gt;;                   &lt;span class=&quot;co1&quot;&gt;// В HT-PICC&lt;/span&gt;
&lt;span class=&quot;kw4&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; near &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; PORTB;                          &lt;span class=&quot;co1&quot;&gt;// В MCC18&lt;/span&gt;
&lt;span class=&quot;kw4&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; near &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt;  PORTB __attribute__&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;__sfr__&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;; &lt;span class=&quot;co1&quot;&gt;// В MCC30&lt;/span&gt;
и т.д.&lt;/pre&gt;
&lt;p&gt;
(в этом можно убедиться, заглянув в заголовочный файл для конкретного контроллера, поставляемый с компилятором). Квалификатор &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; запрещает производить оптимизацию кода, выполняющего действия над регистром PORTB. Поэтому даже взаимообратные действия останутся нетронутыми оптимизатором, и мы можем быть уверенны в том, что на выходе сформируется импульс.
&lt;/p&gt;

&lt;p&gt;
Итак, выполняя оптимизацию, компиляторы стремятся помочь нам сделать наш код максимально быстрым и максимально компактным. Однако, если не использовать &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;, в некоторых случаях оптимизация может сыграть с программистом злую шутку. Причем, надо сказать, чем умнее и мощнее компилятор, тем больше проблем он может создать при неграмотном использовании &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Определение&quot; [3584-9638] --&gt;
&lt;h1&gt;&lt;a name=&quot;ошибки_связанные_с_volatile&quot; id=&quot;ошибки_связанные_с_volatile&quot;&gt;Ошибки, связанные с volatile&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;

Есть три основных типа ошибок, касающихся квалификатора &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;неиспользование volatile там, где нужно&lt;/strong&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; обычно совершается программистами, которые не знают про существование &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;, или видели, но не понимают, что это такое;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;использование volatile там, где нужно, но не так, как нужно&lt;/strong&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; присуща программистам, знающим, насколько важен &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; при программировании параллельных процессов или при доступе к периферийным регистрам, но не учитывающие некоторые его нюансы;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;использование volatile там, где не нужно (бывает и такое)&lt;/strong&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; такое делают те, кто однажды обжегся на первых двух ошибках. Это не ошибка и она не приведет к неправильному поведению программы, но создаст свои неприятности.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Ошибки, связанные с volatile&quot; [9639-11030] --&gt;
&lt;h2&gt;&lt;a name=&quot;неиспользование_volatile&quot; id=&quot;неиспользование_volatile&quot;&gt;Неиспользование volatile&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Неиспользование volatile&quot; [11031-11083] --&gt;
&lt;h3&gt;&lt;a name=&quot;глобальные_переменные&quot; id=&quot;глобальные_переменные&quot;&gt;Глобальные переменные&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Самая частая ошибка, связанная с &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;, это просто неиспользование этого квалификатора там, где это нужно. Работая с микроконтроллерами, программист почти всегда сам является автором кода основной программы и кода для обработки прерываний. Причем основная программа и прерывания должны обмениваться данными, а самым распространенным способом для этого является использование глобальных переменных (будь то счетчики, привязанные к периоду переполнения таймера, или буферы для хранения входных/выходных данных, или переменные состояния, или еще что-то).
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;модификация_переменной_в_прерывании&quot; id=&quot;модификация_переменной_в_прерывании&quot;&gt;Модификация переменной в прерывании&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Рассмотрим пример для PIC24:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; Counter;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; __attribute__&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;__interrupt__, __auto_psv__&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; _T1Interrupt &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    IFS0bits.&lt;span class=&quot;me1&quot;&gt;T1IF&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
    Counter++;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; wait &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;kw3&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    Counter &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Counter &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;kw3&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;continue&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
При отключенной оптимизации данный код будет работать. Но стоит включить оптимизацию, как программа начнет зависать в функции wait(). Что происходит? Компилятор, транслируя функцию wait(), не знает про то, что переменная Counter может измениться в любой момент при возникновении прерывания. Он видит только то, что мы ее обнуляем, а затем сразу сравниваем с параметром Time. Другими словами компилятор предполагает, что переменная Time всегда сравнивается с нулем, и листинг функции wait() при включенной оптимизации будет выглядеть так:
&lt;/p&gt;
&lt;pre class=&quot;asm code asm&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;nu0&quot;&gt;0x5B4&lt;/span&gt;   &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt;   clr&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   Counter
&lt;span class=&quot;nu0&quot;&gt;0x5B6&lt;/span&gt;           cp0&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   w0          &lt;span class=&quot;co1&quot;&gt;; &amp;lt;-----&lt;/span&gt;
&lt;span class=&quot;nu0&quot;&gt;0x5B8&lt;/span&gt;           bra     nz&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;0x5B6&lt;/span&gt;
&lt;span class=&quot;nu0&quot;&gt;0x5BA&lt;/span&gt;           return&lt;/pre&gt;
&lt;p&gt;
 
(Примечание: компилятор Си единственный однобайтовый аргумент передает в функцию через регистр w0)
&lt;/p&gt;

&lt;p&gt;
Что мы видим в этом коде: производится обнуление переменной Counter, а затем параметр функции Time, переданный в нее через регистр w0, сравнивается с нулем, больше не обращая внимания на истинное значение переменной Coutner, которая исправно увеличивается при каждом прерывании по таймеру. Другими словами, мы попали в вечный цикл. Как уже говорилось, дело в том, что компилятор не предполагает, что функция будет прервана каким-то кодом, который будет производить операции над переменными, участвующими в работе функций. Здесь нам на помощь и приходит квалификатор &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; Counter;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; __attribute__&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;__interrupt__&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; _T1Interrupt &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    IFS0bits.&lt;span class=&quot;me1&quot;&gt;T1IF&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
    Counter++;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; wait &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;kw3&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    Counter &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Counter &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;kw3&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;continue&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Теперь при трансляции компилятор сгенерит код, который будет каждый раз обращаться к переменной Counter:
&lt;/p&gt;
&lt;pre class=&quot;asm code asm&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;nu0&quot;&gt;0x5B4&lt;/span&gt;   &lt;span class=&quot;kw1&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt;   &lt;span class=&quot;kw1&quot;&gt;mov&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   w0&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; w1
&lt;span class=&quot;nu0&quot;&gt;0x5B6&lt;/span&gt;           clr&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   Counter
&lt;span class=&quot;nu0&quot;&gt;0x5B8&lt;/span&gt;           &lt;span class=&quot;kw1&quot;&gt;mov&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   Counter&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; w0      &lt;span class=&quot;co1&quot;&gt;; &amp;lt;-----&lt;/span&gt;
&lt;span class=&quot;nu0&quot;&gt;0x5BA&lt;/span&gt;           &lt;span class=&quot;kw1&quot;&gt;sub&lt;/span&gt;     w0&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; w1&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;w15&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;
&lt;span class=&quot;nu0&quot;&gt;0x5BC&lt;/span&gt;           bra     nc&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;0x5B8&lt;/span&gt;
&lt;span class=&quot;nu0&quot;&gt;0x5BE&lt;/span&gt;           return&lt;/pre&gt;
&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;модификация_переменной_в_параллельной_задаче&quot; id=&quot;модификация_переменной_в_параллельной_задаче&quot;&gt;Модификация переменной в параллельной задаче&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Если программа работает под управлением ОСРВ, т.е. выполнение функции может быть прервано в какой-то момент, а потом опять передано ей с того места, где она прервалась. Не важно, кооперативный ли планировщик у ОС (т.е. сам программист решает, где функции быть прерванной) или вытесняющий (тут программист вообще ничего не решает, и его функция может быть прервана абсолютно в любой момент времени более приоритетной задачей). Важно то, что компилятор не знает о том, что в середине функции может быть выполнен какой-то посторонний код. Вот фрагмент кода из одной реальной программы:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; Button;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task_Button &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; NewLevel&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt; Temp;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//...&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;// Переключение контекста&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//...&lt;/span&gt;
&amp;nbsp;
        Temp &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; PORTB;
        &lt;span class=&quot;co1&quot;&gt;//...&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;// Обработка дребезга&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//...&lt;/span&gt;
&amp;nbsp;
        Button &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Temp &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; Button &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Temp &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; Button &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Temp &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; Button &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task_Work &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;Button&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; PIN_RED_LED &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//...&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;// Переключение контекста&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;// и пр.&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//...&lt;/span&gt;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Button&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
            &lt;span class=&quot;kw1&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;co1&quot;&gt;//... Действия по кнопке 1&lt;/span&gt;
                    &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;
            &lt;span class=&quot;kw1&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;co1&quot;&gt;//... Действия по кнопке 2&lt;/span&gt;
                    &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;
            &lt;span class=&quot;kw1&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;co1&quot;&gt;//... Действия по кнопке 3&lt;/span&gt;
                    &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Программа хорошо работала, будучи собранной компилятором HT-PICC18, но при переносе этого же кода на PIC24 (компилятор MCC30) работать перестала, т.е. совсем не реагировала на кнопки. Проблема была в том, что оптимизатор MCC30, в отличие от оптимизатора HT-PICC18, учел, что на момент выполнения switch значение переменной Button уже хранится в одном из регистров общего назначения (w0) (в PIC18 всего 1 аккумулятор, поэтому при работе с ним такое поведение менее вероятно):
&lt;/p&gt;
&lt;pre class=&quot;asm code asm&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co1&quot;&gt;;                            if (!Button) PIN_RED_LED = 1;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;mov&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   Button&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; w0
    bra     nz&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1f&lt;/span&gt;
    bset&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b  PORTB&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;sy0&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;sy0&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;;                            switch (Button)&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   w0&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; #&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;w15&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;
    bra     z&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; Case2
    &lt;span class=&quot;kw1&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   w0&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; #&lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;w15&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;
    bra     z&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; Case3
    &lt;span class=&quot;kw1&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   w0&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; #&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;w15&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;
    bra     nz&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; SwithEnd
Case1&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;sy0&quot;&gt;...&lt;/span&gt;
Case2&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;sy0&quot;&gt;...&lt;/span&gt;
Case3&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;sy0&quot;&gt;...&lt;/span&gt;
SwithEnd&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Автор этого кода рассказал, что при отладке сломал кнопку, пытаясь нажать ее сильнее, чтобы она хоть как-то сработала :). Его ошибка заключалась в том, что он не использовал &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; при объявлении переменной Button. Сделай он это - и компилятор знал бы, что при каждом обращении к переменной Button нужно выполнять инструкции для фактического обращения к ячейке памяти, а не использовать для ускорения промежуточный результат.
&lt;/p&gt;

&lt;p&gt;
После правильного объявления переменной:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; Button;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task_Button &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; NewLevel&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    ...
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task_Work &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    ...
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
проблема исчезла:
&lt;/p&gt;
&lt;pre class=&quot;asm code asm&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co1&quot;&gt;;                            if (!Button) PIN_RED_LED = 1;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;mov&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   Button&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; w0
    bra     nz&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1f&lt;/span&gt;
    bset&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b  PORTB&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;sy0&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;sy0&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;;                            switch (Button)&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;mov&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   Button&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; w0
    &lt;span class=&quot;kw1&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   w0&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; #&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;w15&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;
    bra     z&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; Case2
    &lt;span class=&quot;kw1&quot;&gt;mov&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   Button&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; w0
    &lt;span class=&quot;kw1&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   w0&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; #&lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;w15&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;
    bra     z&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; Case3
    &lt;span class=&quot;kw1&quot;&gt;mov&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   Button&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; w0
    &lt;span class=&quot;kw1&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   w0&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; #&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;w15&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;
    bra     nz&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; SwithEnd
Case1&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;sy0&quot;&gt;...&lt;/span&gt;
Case2&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;sy0&quot;&gt;...&lt;/span&gt;
Case3&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;sy0&quot;&gt;...&lt;/span&gt;
SwithEnd&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Глобальные переменные&quot; [11084-20385] --&gt;
&lt;h3&gt;&lt;a name=&quot;локальные_переменные&quot; id=&quot;локальные_переменные&quot;&gt;Локальные переменные&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Часто для создания небольших задержек пользуются такими функциями:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Delay &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; D&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; i;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; i &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; D; i&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;continue&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;

Однако, некоторые компиляторы видят в этих функциях бесполезный код и вообще не включают его в результирующий объектный код. Если эта задержка применялась для снижения скорости программного i2c (или SPI) под компилятором, например, HT-PICC, то при переносе на ядро AVR с компилятором WinAVR программа перестанет работать, вернее, она будет работать так быстро, что управляемая микросхема не будет успевать обрабатывать сигналы из-за того, что все вызовы функции Delay будут упразднены.
&lt;/p&gt;

&lt;p&gt;
Чтобы этого не происходило, нужно использовать квалификатор &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Delay &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; D&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; i;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;; i &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; D; i&lt;span class=&quot;sy2&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;continue&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Локальные переменные&quot; [20386-21771] --&gt;
&lt;h3&gt;&lt;a name=&quot;указатели_на_volatile&quot; id=&quot;указатели_на_volatile&quot;&gt;Указатели на volatile&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Также распространенной ошибкой является использование обычного указателя (не указателя на &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;) на &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;-переменную. Чем это может нам навредить? Рассмотрим пример (из реальной программы), в котором был использован указатель на регистр порта ввода/вывода. Программа по SPI управляла двумя одинаковыми микросхемами, подключенными на разные выводы микроконтроллера, порт, к которому подключена активная микросхема, выбирался через указатель:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;Port;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; SPI_Send &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; Data&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;8&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Data &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x80&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;Port |=  &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;      &lt;span class=&quot;co1&quot;&gt;// Set data bit&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;             &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;Port &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; ~&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;Port |=  &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;;                       &lt;span class=&quot;co1&quot;&gt;// Make clock pulse&lt;/span&gt;
        Data &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;                        &lt;span class=&quot;co1&quot;&gt;// Shift data&lt;/span&gt;
        &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;Port &lt;span class=&quot;sy3&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; ~&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;--&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; main &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//...&lt;/span&gt;
    Port &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;PORTB;
    &lt;span class=&quot;co1&quot;&gt;//...&lt;/span&gt;
    SPI_Send&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu12&quot;&gt;0x55&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    SPI_Send&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu12&quot;&gt;0x66&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;co1&quot;&gt;//...&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Произошла та же ситуация, что и в предыдущем примере: под одним компилятором (MCC18) код работал, а под другим (MCC30) - перестал. Причина крылась в неправильно объявленном указателе. Дизассемблер функции SPI_Send() выглядел так:
&lt;/p&gt;
&lt;pre class=&quot;asm code asm&quot; style=&quot;font-family:monospace;&quot;&gt;   0050C   SPI_Send&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt;        &lt;span class=&quot;kw1&quot;&gt;mov&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;w   port&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;w1
   0050E                    &lt;span class=&quot;kw1&quot;&gt;mov&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   #&lt;span class=&quot;nu0&quot;&gt;0x8&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;w2         &lt;span class=&quot;co1&quot;&gt;;    i = 8&lt;/span&gt;
   &lt;span class=&quot;nu0&quot;&gt;00510&lt;/span&gt;   &lt;span class=&quot;kw4&quot;&gt;While&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt;           cp0&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   w0&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;#&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;           &lt;span class=&quot;co1&quot;&gt;;      if (Data &amp;amp; 0x80)&lt;/span&gt;
   &lt;span class=&quot;nu0&quot;&gt;00512&lt;/span&gt;                    bra     ges&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;0x000518&lt;/span&gt;
   &lt;span class=&quot;nu0&quot;&gt;00514&lt;/span&gt;                    bset    &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;w1&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;#&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;         &lt;span class=&quot;co1&quot;&gt;;        *Port |=  1&lt;/span&gt;
   &lt;span class=&quot;nu0&quot;&gt;00516&lt;/span&gt;                    bra     &lt;span class=&quot;nu0&quot;&gt;0x00051a&lt;/span&gt;
   &lt;span class=&quot;nu0&quot;&gt;00518&lt;/span&gt;                    bclr    &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;w1&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;#&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;         &lt;span class=&quot;co1&quot;&gt;;        *Port &amp;amp;= ~1&lt;/span&gt;
                                                    &lt;span class=&quot;co1&quot;&gt;;        *Port |=  2     &amp;lt;=======&lt;/span&gt;
   0051A                    &lt;span class=&quot;kw1&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   w0&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;w0&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;w0        &lt;span class=&quot;co1&quot;&gt;;        Data &amp;lt;&amp;lt;= 1&lt;/span&gt;
   0051C                    bclr    &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;w1&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;#&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;         &lt;span class=&quot;co1&quot;&gt;;        *Port &amp;amp;= ~2&lt;/span&gt;
   0051E                    &lt;span class=&quot;kw1&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   w2&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;w2           &lt;span class=&quot;co1&quot;&gt;;    while (--n)&lt;/span&gt;
   &lt;span class=&quot;nu0&quot;&gt;00520&lt;/span&gt;                    bra nz&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;0x000510&lt;/span&gt;
   &lt;span class=&quot;nu0&quot;&gt;00522&lt;/span&gt;                    return&lt;/pre&gt;
&lt;p&gt;
Обратим внимание на то, что компилятор &amp;quot;выкинул&amp;quot; установку бита 1 (&amp;quot;*Port |= 2&amp;quot;), посчитав ее лишней, т.к. почти сразу же за ней этот бит снова обнуляется, т.е. он выполнил оптимизацию без учета особенностей регистра, на который указывала переменная Port, а сделал он так потому, что программист не объяснил компилятору, что регистр непростой. Для исправления ошибки нужно было объявить переменную Port как указатель на &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; переменную:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;Port;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; SPI_Send &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; Data&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    ...
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Теперь компилятор знает, что оптимизацию над переменной, на которую указывает Port, производить нельзя. И новый листинг это отражает:
&lt;/p&gt;
&lt;pre class=&quot;asm code asm&quot; style=&quot;font-family:monospace;&quot;&gt;   0050C   SPI_Send&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt;        &lt;span class=&quot;kw1&quot;&gt;mov&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;w   port&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;w1
   0050E                    &lt;span class=&quot;kw1&quot;&gt;mov&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   #&lt;span class=&quot;nu0&quot;&gt;0x8&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;w2         &lt;span class=&quot;co1&quot;&gt;;    i = 8&lt;/span&gt;
   &lt;span class=&quot;nu0&quot;&gt;00510&lt;/span&gt;   &lt;span class=&quot;kw4&quot;&gt;While&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;:&lt;/span&gt;           cp0&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   w0              &lt;span class=&quot;co1&quot;&gt;;      if (Data &amp;amp; 0x80)&lt;/span&gt;
   &lt;span class=&quot;nu0&quot;&gt;00512&lt;/span&gt;                    bra     ges&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;0x000518&lt;/span&gt;
   &lt;span class=&quot;nu0&quot;&gt;00514&lt;/span&gt;                    bset    &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;w1&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;#&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;         &lt;span class=&quot;co1&quot;&gt;;        *Port |=  1&lt;/span&gt;
   &lt;span class=&quot;nu0&quot;&gt;00516&lt;/span&gt;                    bra     &lt;span class=&quot;nu0&quot;&gt;0x00051a&lt;/span&gt;
   &lt;span class=&quot;nu0&quot;&gt;00518&lt;/span&gt;                    bclr    &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;w1&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;#&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;         &lt;span class=&quot;co1&quot;&gt;;        *Port &amp;amp;= ~1&lt;/span&gt;
   0051A                    bset    &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;w1&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;#&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;         &lt;span class=&quot;co1&quot;&gt;;        *Port |=  2    &amp;lt;-----------&lt;/span&gt;
   0051C                    &lt;span class=&quot;kw1&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   w0&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;w0&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;w0        &lt;span class=&quot;co1&quot;&gt;;        Data &amp;lt;&amp;lt;= 1&lt;/span&gt;
   0051E                    bclr    &lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;w1&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;#&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;         &lt;span class=&quot;co1&quot;&gt;;        *Port &amp;amp;= ~2&lt;/span&gt;
   &lt;span class=&quot;nu0&quot;&gt;00520&lt;/span&gt;                    &lt;span class=&quot;kw1&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   w2&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;w2           &lt;span class=&quot;co1&quot;&gt;;    while (--n)&lt;/span&gt;
   &lt;span class=&quot;nu0&quot;&gt;00522&lt;/span&gt;                    bra nz&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;0x000510&lt;/span&gt;
   &lt;span class=&quot;nu0&quot;&gt;00524&lt;/span&gt;                    return&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Указатели на volatile&quot; [21772-26486] --&gt;
&lt;h3&gt;&lt;a name=&quot;аргумент_функции&quot; id=&quot;аргумент_функции&quot;&gt;Аргумент функции&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Если бы в функцию SPI_Send из предыдущего примера нужно было бы передавать адрес порта (т.е. не держать его в глобальной переменной, а передавать в качестве аргумента), то не нужно забывать, что сам аргумент функции должен быть также описан с квалификатором &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; SPI_Send &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; Data, &lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;Port&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
   ...
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; main &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//...&lt;/span&gt;
    SPI_Send&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu12&quot;&gt;0x55&lt;/span&gt;, &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;PORTB&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    SPI_Send&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu12&quot;&gt;0x66&lt;/span&gt;, &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt;PORTB&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;co1&quot;&gt;//...&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;

В противном случае мы получим все те же ошибки, что и в предыдущем примере.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Аргумент функции&quot; [26487-27357] --&gt;
&lt;h2&gt;&lt;a name=&quot;неправильное_использование_volatile&quot; id=&quot;неправильное_использование_volatile&quot;&gt;Неправильное использование volatile&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Неправильное использование volatile&quot; [27358-27431] --&gt;
&lt;h3&gt;&lt;a name=&quot;volatile_атомарность&quot; id=&quot;volatile_атомарность&quot;&gt;volatile != атомарность&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Существует некоторое заблуждение насчет защищенности переменных, объявленных как &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;. Т.е. программист, используя &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;-переменную, уверен, что компилятор сам позаботится о том, чтобы операции с переменной защищались запретами прерываний или еще какими-то хитрыми действиями. Однако, это не так. Объявление переменной как &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; совсем не гарантирует нам атомарность операций с ней. Тут программистов подстерегают две проблемы: 
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; обращение к многобайтовой переменной;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; чтение/модификация/запись через аккумулятор.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Рассмотрим пример обращения к переменной, занимающей более чем одну ячейку памяти (для 16-разрядных контроллеров это int32, int64, float, double; для 8-разрядных - еще и int16). Я часто приводил этот пример, приведу еще раз (для HT-PICC18):
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt; ADCValue;    &lt;span class=&quot;co1&quot;&gt;// Результат чтения АЦП (производится в прерывании)&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; interrupt isr &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;ADIF &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; ADIE&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        ADIF     &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
        ADCValue &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;ADRESH &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; | ADRESL;  &lt;span class=&quot;co1&quot;&gt;// Читаем последнее измерение&lt;/span&gt;
        ADGO     &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;                       &lt;span class=&quot;co1&quot;&gt;// Запускаем следующее&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; main &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//...&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//...&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;ADCValue &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; Alarm&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;       &lt;span class=&quot;co1&quot;&gt;// Сравнение напряжения с пороговыми&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;ADCValue &lt;span class=&quot;sy1&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;900&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; Alarm&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;       &lt;span class=&quot;co1&quot;&gt;// значениями и вызов тревоги&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//...&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Обратим внимание, что переменная ADCValue имеет размерность 2 байта (для хранения 10-битного результата АЦП). Чем опасен данный код? Рассмотрим листинг одного из сравнений (допустим, первого):
&lt;/p&gt;
&lt;pre class=&quot;asm code asm&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co1&quot;&gt;;                  if (ADCValue &amp;lt; 100) Alarm();&lt;/span&gt;
    MOVLW    &lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;
    SUBWF    ADCValue &lt;span class=&quot;sy0&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; W
    MOVLW    &lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt;
    BTFSC    STATUS&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; Z
    SUBWF    ADCValue&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; W
    BTFSS    STATUS&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kw5&quot;&gt;C&lt;/span&gt;
    RCALL    Alarm&lt;/pre&gt;
&lt;p&gt;
Допустим, значение напряжения на входе АЦП такое, что результат преобразования равен 255 (0x0FF). И последнее значение переменной ADCValue, соответственно, тоже = 0x0FF. С этим значением начинает выполняться код сравнения со значением 100 (0x064). Сначала сравниваются старшие байты переменной и константы (0x00 с 0x00), а затем - младшие (0x64 и 0xFF). Результат, казалось бы, очевиден. Однако здесь кроется неприятность. Хоть результат АЦ-преобразования и равен 0xFF, на него влияют несколько факторов: стабильность напряжения питания (или опорного напряжения), стабильность входного сигнала, близость уровня измеряемого напряжения к порогу смены единицы младшего разряда, наводки, шумы и пр. Поэтому результат АЦ-преобразования имеет некий джиттер в одну-две единицы младшего разряда. Т.е. результат АЦП может скакать между значениями 0xFF и 0x100. И если между выполнением сравнений возникнет прерывание, то может произойти следующее:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; значение ADCValue = 0x0FF;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; произведено сравнение старших байтов: 0x00 и 0x00;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; возникло прерывание по ADIF, в котором значение переменной ADCValue обновилось на 0x100;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; производится сравнение младших байтов: 0x64 и уже 0x00!&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; т.к. программа думает, что было сравнение 0x000 &amp;lt; 0x064, то она вызывает функцию Alarm.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

И квалификатор &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; здесь не спасает. Здесь спасет только запрет прерываний на время выполнения сравнений.
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;        ADIE &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;ADCValue &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt; ADIE &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;  Alarm&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;ADCValue &lt;span class=&quot;sy1&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;900&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt; ADIE &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;  Alarm&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
        ADIE &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
...
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Alarm &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    ADIE &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;		&lt;span class=&quot;co1&quot;&gt;// Не забыть включить прерывание при выполнении условий&lt;/span&gt;
    ...&lt;/pre&gt;
&lt;p&gt;
Так может быть, &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; вообще не нужен? Прерывания-то все равно запрещаются? Да, прерывания запрещаются, но &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;, все-таки нужен. Зачем? Рассмотрим почти такой же код для компилятора C30:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt; ADCValue;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; main &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;co1&quot;&gt;//...&lt;/span&gt;
    Temp &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; ADCValue;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;co1&quot;&gt;//...&lt;/span&gt;
        IEC0bits.&lt;span class=&quot;me1&quot;&gt;ADIE&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;ADCValue &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt; IEC0bits.&lt;span class=&quot;me1&quot;&gt;ADIE&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;; Alarm&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;ADCValue &lt;span class=&quot;sy1&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;900&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt; IEC0bits.&lt;span class=&quot;me1&quot;&gt;ADIE&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;; Alarm&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
        IEC0bits.&lt;span class=&quot;me1&quot;&gt;ADIE&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
        &lt;span class=&quot;co1&quot;&gt;//...&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Вот здесь-то volatile и пригодится! Обратим внимание на строчку перед вечным циклом - присваивание значения переменной ADCValue переменной Temp. А заодно посмотрим листинг:
&lt;/p&gt;
&lt;pre class=&quot;asm code asm&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;0053&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;6&lt;/span&gt;      &lt;span class=&quot;kw1&quot;&gt;mov&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;w    ADCValue&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; w1
&lt;span class=&quot;co2&quot;&gt;0053&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;8&lt;/span&gt;      &lt;span class=&quot;kw1&quot;&gt;mov&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;w    w1&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; Temp
&lt;span class=&quot;co1&quot;&gt;;                                   while (1)&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;;                                   {&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;;                                       IEC0bits.AD1IE = 0;&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;0053&lt;/span&gt;A      bclr&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   &lt;span class=&quot;nu0&quot;&gt;0x0095&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;#&lt;span class=&quot;nu0&quot;&gt;5&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;;                                       if (ADCValue &amp;lt; 100) { IEC0bits.AD1IE = 1; Alarm();}&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;0053&lt;/span&gt;&lt;span class=&quot;kw5&quot;&gt;C&lt;/span&gt;      &lt;span class=&quot;kw1&quot;&gt;mov&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;w    #&lt;span class=&quot;nu0&quot;&gt;0x63&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;w0
&lt;span class=&quot;co2&quot;&gt;0053&lt;/span&gt;E      &lt;span class=&quot;kw1&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;w    w1&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;w0&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;w15&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;0054&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;0&lt;/span&gt;      bra      leu&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;0x000548&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;;                                       if (ADCValue &amp;gt; 900) { IEC0bits.AD1IE = 1; Alarm();}&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;0054&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;      &lt;span class=&quot;kw1&quot;&gt;mov&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;w    #&lt;span class=&quot;nu0&quot;&gt;0x384&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;w0
&lt;span class=&quot;co2&quot;&gt;0054&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;      &lt;span class=&quot;kw1&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;w    w1&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;w0&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;w15&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;0054&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;6&lt;/span&gt;      bra      leu&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;0x00054c&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;0054&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;8&lt;/span&gt;      bset&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b   &lt;span class=&quot;nu0&quot;&gt;0x0095&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;#&lt;span class=&quot;nu0&quot;&gt;5&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;0054&lt;/span&gt;A      rcall    Alarm
&lt;span class=&quot;co1&quot;&gt;;                                       IEC0bits.AD1IE = 1;&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;0054&lt;/span&gt;&lt;span class=&quot;kw5&quot;&gt;C&lt;/span&gt;      bset&lt;span class=&quot;sy0&quot;&gt;.&lt;/span&gt;b &lt;span class=&quot;nu0&quot;&gt;0x0095&lt;/span&gt;&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt;#&lt;span class=&quot;nu0&quot;&gt;5&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Как видим, внутри цикла вообще нет обращения к переменной ADCValue, а вместо этого сравнение производится с регистром W1, куда была скопирована переменная ADCValue еще перед циклом. Поэтому, как ни будет изменяться ADCValue, наша программа этого не заметит. Так что &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; в данном случае нужен обязательно, просто не следует забывать, что этот квалификатор не гарантирует нам атомарности операций над объявленной переменной.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;volatile != атомарность&quot; [27432-35431] --&gt;
&lt;h3&gt;&lt;a name=&quot;volatile_указатели&quot; id=&quot;volatile_указатели&quot;&gt;volatile указатели&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Иногда бывает такое, что сам указатель на какую-то переменную должен быть защищен от оптимизации (например, есть его модификация в теле прерывания). Часто программисты совершают одну и ту же ошибку, т.е. при объявлении такого указателя ставят &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; не туда, куда нужно:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;p;
или
&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;p;&lt;/pre&gt;
&lt;p&gt;

Обе записи идентичны, но они не делают саму переменную p &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;-переменной. Обе записи означают &amp;quot;переменная p является указателем на &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; char&amp;quot;. Последствия такого определения очевидны: при изменении значения указателя в прерывании или в параллельной задаче программа этого может не заметить, т.к. будет работать с указателем через РОН.
&lt;/p&gt;

&lt;p&gt;
Правильное определение выглядит так:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; p;&lt;/pre&gt;
&lt;p&gt;
Если же нужен &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;-указатель на &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;-переменную, то он объявляется так:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; p;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;volatile указатели&quot; [35432-37038] --&gt;
&lt;h2&gt;&lt;a name=&quot;использование_volatile_не_к_месту&quot; id=&quot;использование_volatile_не_к_месту&quot;&gt;Использование volatile не к месту&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Скажу два слова об этой проблеме. У некоторых однажды нарвавшихся на проблемы, связанные с отсутствием &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; там, где он должен был быть, появляется какой-то комплекс &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;-мании (или оптимизация-фобии), когда в страхе, что &amp;quot;а вдруг компилятор этот код выкинет&amp;quot;, чуть ли не все переменные объявляются с квалификатором &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;. В принципе, ничего криминального в таком использовании &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; нет, код будет работать правильно, но он будет громоздкий и неповоротливый. &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; не позволит компилятору применять оптимизацию, и некоторые фрагменты, которые, будучи оптимизированы, выполнились бы за 50-100 тактов, будут выполняться за 1000-2000 тактов. То же самое касается объемов кода (в десятки раз он, конечно, не вырастет, а вот раза в два - запросто). Другими словами появится неудовлетворенность оттого, что &lt;em&gt;&amp;quot;и контроллер быстрый, и компилятор хороший, а код все равно тормозит&amp;quot;&lt;/em&gt;.
&lt;/p&gt;

&lt;p&gt;
Здесь четких рекомендаций я дать не смогу. В большинстве случаев при написании программ на Си я стараюсь придерживаться следующих правил:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; глобальные переменные, используемые и в прерываниях, и в программе (или в прерываниях различных приоритетов), нужно объявлять как &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; глобальные переменные, обрабатываемые двумя и более задачами при работе под многозадачной ОС, нужно объявлять как &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; указатели на периферийные регистры, а также на переменные, объявленные как &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;, нужно объявлять как указатели на &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; все, что не попадает под первые три правила и не связано с периферией, рекомендуется писать, абстрагируясь от железа. Тогда становится понятно, что циклы вида &amp;quot;for (i = 0; i&amp;lt;100; i++) {};&amp;quot; не несут на себе никакой алгоритмической нагрузки и могут быть удалены. А если они должны быть оставлены, то переменные следует объявлять как &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;. &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; во всех остальных случаях &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; будет лишним.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Использование volatile не к месту&quot; [37039-40587] --&gt;
&lt;h1&gt;&lt;a name=&quot;дополнительно&quot; id=&quot;дополнительно&quot;&gt;Дополнительно&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;

Еще несколько замечаний:
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;volatile_можно_ставить_в_любое_место_в_типе&quot; id=&quot;volatile_можно_ставить_в_любое_место_в_типе&quot;&gt;volatile можно ставить в любое место в типе&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Как уже указывалось выше, следующие записи эквивалентны:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt; N;
&lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt; N;
&lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; N;&lt;/pre&gt;
&lt;p&gt;
Лично я для наглядности &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; выношу вперед.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;volatile_и_typedef&quot; id=&quot;volatile_и_typedef&quot;&gt;volatile и typedef&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

&lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; может быть использован при определении типов по всем правилам:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; vchar;&lt;/pre&gt;
&lt;p&gt;
Теперь все переменные типа vchar будут &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;-переменными.
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;volatile_и_структуры&quot; id=&quot;volatile_и_структуры&quot;&gt;volatile и структуры&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

&lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; может быть применен как ко всей структуре целиком, так и к отдельным ее полям. В зависимости от этого компилятор будет производить оптимизацию по-разному. Например, в этой структуре все поля будут &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;struct&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;  Counter;
    &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;Buffer;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; T_BUFFER;&lt;/pre&gt;
&lt;p&gt;
(Обращу ваше внимание на то, что здесь Buffer является &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;-указателем на не-&lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt; переменную. Т.е. эквивалент определения: &amp;quot;unsigned char * volatile Buffer&amp;quot;, а не &amp;quot;volatile unsigned char *Buffer&amp;quot;.)
&lt;/p&gt;

&lt;p&gt;
А в этой - только счетчик:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;struct&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
     &lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt;  Counter;
              &lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt;Butter;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; T_BUFFER;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;volatile_и_extern&quot; id=&quot;volatile_и_extern&quot;&gt;volatile и extern&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Если переменная во внешнем модуле объявлена как &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt; Counter;&lt;/pre&gt;
&lt;p&gt;
, то и в заголовочном файле она должна быть объявлена с квалификатором &lt;span style='color:green; '&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/span&gt;:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt; Counter;&lt;/pre&gt;&lt;hr /&gt;

&lt;p&gt;

&lt;br/&gt;
 
&lt;br/&gt;
 
&lt;br/&gt;
 
&lt;/p&gt;

&lt;p&gt;
Виктор Тимофеев, июнь, 2010&lt;br/&gt;
 
&lt;a href=&quot;mailto:&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot; class=&quot;mail JSnocheck&quot; title=&quot;&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot;&gt;osa@pic24.ru&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Дополнительно&quot; [40588-] --&gt;</description>
    </item>
    <item rdf:about="http://www.pic24.ru/doku.php/osa/articles/wdt?rev=1378319751">
        <dc:format>text/html</dc:format>
        <dc:date>2013-09-04T22:35:51+03:00</dc:date>
        <title>5.4 Сторожевой таймер</title>
        <link>http://www.pic24.ru/doku.php/osa/articles/wdt?rev=1378319751</link>
        <description>


&lt;h1&gt;&lt;a name=&quot;сторожевой_таймер&quot; id=&quot;сторожевой_таймер&quot;&gt;5.4 Сторожевой таймер&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;p&gt;

&lt;span style='color:red; '&gt;Данная статья является наброском к главе &amp;quot;5.4 Сторожевой таймер&amp;quot; книги &amp;quot;Отказоустойчивое ПО для МК&amp;quot;. &lt;/span&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;5.4 Сторожевой таймер&quot; [1-255] --&gt;
&lt;h2&gt;&lt;a name=&quot;определение&quot; id=&quot;определение&quot;&gt;Определение&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

&lt;strong&gt;WDT&lt;/strong&gt; - сторожевой таймер (watchdog timer) - аппаратный механизм защиты от зависания программы. В самом простом случае WDT представляет собой счетчик, тактируемый системным или независимым генератором. При переполнении счетчика схема WDT генерирует сигнал сброса микроконтроллера, который приводит к его перезагрузке. Программа должна периодически посылать схеме WDT сигнал о том, что все в порядке; этот сигнал говорит сторожевому таймеру, что программа работает корректно, и счетчик сторожевого таймера обнуляется (в некоторых контроллерах обновляется константой). 
&lt;/p&gt;

&lt;p&gt;
Если программа перестает посылать сигналы сторожевому таймеру, т.е. сигналы обнуления WDT отсутствуют в течение времени, достаточного для переполнения счетчика, то это свидетельствует о том, что программа зависла, и ситуация воспринимается как аварийная. В результате длительного отсутствия сигналов от программы схема WDT формирует сигнал сброса микроконтроллера (в некоторых типах микроконтроллеров WDT генерирует еще немаскируемое прерывание). 
&lt;/p&gt;

&lt;p&gt;
Также многие WDT могут работать в оконном режиме, когда сигнал от программы на обнуление WDT не допускается раньше какого-то времени. Т.е. он не должен приходить слишком рано или слишком поздно. Такая функция может оказаться полезной для более быстрого реагирования на некоторые виды сбоя. Например, если функция обнуления WDT вызывается раньше времени, то это говорит о том, что какие-то функции отработали быстрее, чем ожидалось, что может свидетельствовать о наличии сбоя. Из такого режима нужно выйти как можно быстрее, не дожидаясь переполнения WDT (т.е. не давая программе еще какое-то время работать во внештатном режиме). Оконные WDT удобно применять в тех случаях, когда нет возможности контролировать время выполнения участков кода с помощью аппаратного таймера. 
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Определение&quot; [256-3536] --&gt;
&lt;h2&gt;&lt;a name=&quot;типы_wdt&quot; id=&quot;типы_wdt&quot;&gt;Типы WDT&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

WDT бывает встроенным в МК, внешним в виде специализированной микросхемы или внешним в виде отдельного электронного устройства (или узла). Обычно счетчик в составе WDT тактируется собственным генератором, не зависящим от основного системного генератора контроллера. Чаще всего это внутренний RC-генератор. Отдельный генератор применяется с той целью, чтобы WDT имел возможность продолжать счет (а в последствии - сформировать сигнал сброса) даже если произойдет срыв генерации основного генератора, тактирующего МК.
&lt;/p&gt;

&lt;p&gt;
В зависимости от типа WDT обнуление его счетчика может производиться по-разному. Например, счетчик внешнего WDT обычно сбрасывается переключением управляющего входа из одного состояния в другое (в некоторых - только по одному фронту). Для сброса внутренних WDT может использоваться специальная инструкция микроконтроллера, или запись в какой-нибудь регистр (или отдельный бит регистра), или выполнение микроконтроллером последовательной записи двух констант (так называемого  пароля) в определенный регистр WDT. 
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Типы WDT&quot; [3537-5438] --&gt;
&lt;h3&gt;&lt;a name=&quot;внутренний_wdt&quot; id=&quot;внутренний_wdt&quot;&gt;Внутренний WDT&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Встроенный в МК сторожевой таймер является самым ненадежным из перечисленных и на него можно полагаться только в малоответственных системах. Есть две причины: 
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Таймер управляется регистрами, которые доступны из программы. Следовательно, сбившаяся программа может произвести случайную запись в эти регистры, изменив тем самым режим работы WDT или вообще отключив его. В большинстве микроконтроллеров операции над регистрами WDT защищены паролем, но это не защищает от случайного попадания программного счетчика на код записи в такие регистры со случайным набором параметров; некоторые микроконтроллеры позволяют производить запись в такие регистры только единожды, однако это не спасает от помехи во время записи в регистр. (Некоторые контроллеры имеют механизмы, запрещающие какие-либо действия по настройки WDT в ходе работы программы.) Также есть ограничение на временной интервал между записью пароля (или его частей) и обновлением регистров WDT. Все эти меры снижают вероятность порчи регистров WDT случайными действиями программы, но не делают это невозможным.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Технологические нормы, по которым производятся сами микросхемы: чем выше степень интеграции, тем тоньше проводники на кристалле и тем более они подвержены влиянию внешних помех. &lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Стоит добавить, что некоторые микроконтроллеры допускают тактирование внутреннего WDT от системного генератора, что делает его неэффективным при срыве генерации или при случайном входе в Sleep-режим.
&lt;/p&gt;

&lt;p&gt;
Поэтому внутренний WDT можно использовать только в малоответственных системах или, на худой конец, там, где критична стоимость продукта, и лишний элемент в виде внешнего WDT будет непозволительной роскошью, или где есть ограничения по габаритам или массе конечного устройства. 
Однако не следует забывать и о достоинствах внутреннего сторожевого таймера: 
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; во-первых, он может пробуждать контроллер из Sleep-режима с заданным интервалом (если WDT тактируется независимым генератором);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; во-вторых, он является единственной возможностью выполнить программный сброс в контроллерах, в которых не предусмотрена инструкция RESET (AVR, PIC16, LPC213x); &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; в-третьих, возможность программного отключения WDT позволяет максимально снизить потребление микроконтроллера за счет отключения независимого генератора;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; в-четвертых, некоторые контроллеры при переполнении WDT перед формированием сигнала сброса генерируют немаскируемое прерывание, которое позволяет успеть выполнить минимальный набор операций по переводу микроконтроллера и каких-то внешних устройств в безопасный режим перед сбросом, а также записать в ОЗУ какие-то данные, которые при перезагрузке могут использоваться для идентификации сбоя и анализа его причин;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; в-пятых, есть возможность изменять интервал в зависимости от режима работы.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Внутренний WDT&quot; [5439-10488] --&gt;
&lt;h3&gt;&lt;a name=&quot;внешний_wdt_в_виде_специализированной_микросхемы&quot; id=&quot;внешний_wdt_в_виде_специализированной_микросхемы&quot;&gt;Внешний WDT в виде специализированной микросхемы&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Более эффективным с точки зрения надежности (и в большинстве случаев достаточным) является внешний WDT, выполненный в виде отдельной микросхемы. Обычно такие микросхемы комбинируют в себе функции сторожевого таймера и монитора напряжения питания. Такие микросхемы (они называются супервизорами, микропроцессорными мониторами или просто сторожевыми таймерами) выпускаются разными фирмами в различных вариантах. Сигналом &amp;quot;все хорошо&amp;quot; сторожевому таймеру в составе такой микросхемы обычно служит изменение логического уровня (&amp;quot;0&amp;quot;→&amp;quot;1&amp;quot; или &amp;quot;1&amp;quot;→&amp;quot;0&amp;quot;) на специальном входе. Изменение уровня сбрасывает внутренний счетчик. Супервизоры имеют выход для управления сбросом микроконтроллера (обычно такие микросхемы имеют два выхода для управления сбросом: прямой и инверсный, - т.к. микроконтроллеры различных производителей имеют различные активные уровни входа сброса). Когда супервизор обнаруживает, что напряжение питания опустилось ниже какого-то предустановленного уровня или сигнал &amp;quot;все хорошо&amp;quot; отсутствует длительное время, он формирует активный уровень на выходе управления сбросом и удерживает его в активном состоянии либо в течение какого-то времени (если произошел сброс по переполнению счетчика сторожевого таймера) либо до того момента, как напряжение питания не придет в норму. Это общая функция, присущая всем супервизорам со встроенным WDT, в остальном микросхемы-супервизоры могут отличаться:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; одни имеют фиксированное время переполнения счетчика WDT, другие имеют возможность выбора интервала из набора фиксированных, третьи имеют возможность настраивать интервал установкой дополнительного конденсатора в цепь задающего RC-генератора;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; одни могут работать как оконные, другие только как традиционные, третьи имеют возможность работать и так и так, причем позволяют выбрать ширину окна;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; некоторые микросхемы имеют дополнительный выход для индикации того, что последний сброс произошел именно по переполнению WDT. Такой выход может оказаться полезным в двух случаях: во-первых, при перезагрузке контроллера мы сможем определить причину сброса, что также позволяет производить тестирование WDT при первом включении; во-вторых, этот выход можно использовать для управления питанием контроллера для вывода его из зависания в случае возникновения эффекта тиристорной защелки;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; некоторые имеют возможность подключать батарейку для продолжения функционирования при отключении питания;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; разные микросхемы имеют различные времена удерживания выхода управления сбросом в активном состоянии, кроме того, некоторые супервизоры позволяют настраивать это время.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

В зависимости от задач, которые мы хотим возложить на WDT, помимо его основной функции, мы можем выбирать ту или иную микросхему. Для управления WDT в составе такой микросхемы нам понадобится один вывод контроллера, настроенный на выход, для генерации сигналов обнуления WDT (в случае контроля выхода WDO потребуется еще один вывод МК, настроенный на вход).
&lt;/p&gt;

&lt;p&gt;
Также при выборе микросхемы супервизора следует обращать внимание на следующие параметры:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; потребление (варьируется от единиц микроампер до единиц миллиампер);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; тип корпуса: от SOT23 до DIP16;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; диапазон рабочих температур;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; цена.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Ниже приведены примеры микросхем-супервизоров, в составе которых имеется WDT (это краткий список, на самом деле таких микросхем великое множество):
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    MCP131x &lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt; интервала WDT &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu16&quot;&gt;6.3&lt;/span&gt;, &lt;span class=&quot;nu0&quot;&gt;102&lt;/span&gt;, &lt;span class=&quot;nu0&quot;&gt;1600&lt;/span&gt;, &lt;span class=&quot;nu0&quot;&gt;25600&lt;/span&gt; мс&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;, потребление &lt;span class=&quot;nu0&quot;&gt;5&lt;/span&gt;мкА, нет ножки WDO
    ADM699 &lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt; фиксированное и довольно длительное время переполнения &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;тип. &lt;span class=&quot;nu16&quot;&gt;1.6&lt;/span&gt;с&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;,
             имеет выход ~WDO, позволяющий определить, что сброс был вызван переполнением WDT.
             Нет внешнего тактирования. Нет входа для батарейки. &lt;span class=&quot;nu0&quot;&gt;600&lt;/span&gt; мкА.
    &lt;span class=&quot;me1&quot;&gt;ADM705&lt;/span&gt;
    MAX69x &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;x &lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt; четный&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt;  Имеет выход ~WDO. Можно подключать батарейку. &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;мА.
    &lt;span class=&quot;me1&quot;&gt;MAX69x&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;x &lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt; нечетный&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt; есть внешнее тактирование, два режима счета &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu16&quot;&gt;1.6&lt;/span&gt;сек или &lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt; ms&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;.
             Имеет выход ~WDO. Можно подключать батарейку. &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;мА.
    &lt;span class=&quot;me1&quot;&gt;EM6151&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt; оконный &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;67&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;33&lt;/span&gt; или &lt;span class=&quot;nu0&quot;&gt;33&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;67&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;, без батарейки, &lt;span class=&quot;nu0&quot;&gt;35&lt;/span&gt; мкА &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt; слип&lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt;режим &lt;span class=&quot;nu0&quot;&gt;25&lt;/span&gt; мкА&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;, нет WDO
    TC1232, ADM1232, MAX1232 &lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt; три режима интервала &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;150&lt;/span&gt;мс, &lt;span class=&quot;nu0&quot;&gt;600&lt;/span&gt;мс, &lt;span class=&quot;nu0&quot;&gt;1200&lt;/span&gt;мс&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;, не имеет выхода WDO
    MAX6746&lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;6753&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;5&lt;/span&gt; мкА, регулируемое время WDT, маленький корпус &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;sot23&lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;, оконный
    MAX6369 &lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;8&lt;/span&gt;мкА, регулируемое время, корпус sot23&lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;8&lt;/span&gt;, есть WDO&lt;/pre&gt;
&lt;p&gt;
&lt;em&gt;TODO: дополнить и разъяснить&lt;/em&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Внешний WDT в виде специализированной микросхемы&quot; [10489-18115] --&gt;
&lt;h3&gt;&lt;a name=&quot;внешний_wdt_в_виде_устройства&quot; id=&quot;внешний_wdt_в_виде_устройства&quot;&gt;Внешний WDT в виде устройства&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Отдельное устройство разрабатывается индивидуально с учетом особенностей конкретного устройства, используемого в нем микроконтроллера и конкретной программы. Сердцем такого устройства может быть маленький, например, 8-разрядный микроконтроллер (чаще OTP, например, PIC12CE518), который, помимо стандартного набора функций WDT, может иметь ряд дополнительных возможностей. Т.е. преимущества таких WDT в расширенном функционале и гибкости настройки. 
&lt;/p&gt;

&lt;p&gt;
Недостатками таких WDT являются габариты, а также то, что программа в маленьком МК должна соответствовать всем требованиям безопасности (начиная с самодиагностики и заканчивая контролем собственного внутреннего WDT), а также требует тщательнейшей отладки. Кроме того, сам микроконтроллер, используемый в качестве WDT, также подвержен всем видам сбоев, вызванных помехами. Но это компенсируется, во-первых, тем, что такие контроллеры выполнены по более грубой технологии, чем тот, которым они будут управлять, следовательно, они помехам подвержены гораздо меньше; во-вторых, есть возможность организовать &amp;quot;круговую поруку&amp;quot;, когда маленький МК следит за большим, а большой следит за маленьким, что во много раз повышает вероятность самовосстановления работоспособности системы после сбоя.
&lt;/p&gt;

&lt;p&gt;
Но построение схем-мониторов (не называю их WDT, т.к. функционал может быть гораздо шире простого счетчика) дает массу дополнительных возможностей, таких как:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;контроль частоты тактирующего генератора&lt;/strong&gt; - это позволит формировать сигнал аварии, если по каким-то причинам тактовая частота контролируемого МК не соответствует ожидаемой (например, частота внутреннего RC-генератора ушла на морозе);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;контроль последовательности действий&lt;/strong&gt; - в некоторых случаях может являться дополнением к run-time тестам, описанным в предыдущем параграфе;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;может принимать более сложные сигналы управления, чем простая смена уровня&lt;/strong&gt; - смена логического уровня (являющаяся сигналом для большинства специализированных микросхем WDT) довольно мало информативна, она может происходить и при сбитой частоте, и при логическом зацикливании программы (когда требуемые действия выполняются, но не в той последовательности);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;может изменять параметры окна в широком диапазоне&lt;/strong&gt; - в зависимости от режима работы целевого МК могут понадобиться различные временные интервалы: где-то скорость реакции на сбой должна быть максимальной, а где-то выполняются длинные критические секции, которые нельзя прерывать;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; &lt;strong&gt;формирование предварительного сигнала для генерации немаскируемого прерывания&lt;/strong&gt; - крайне полезная функция, которая позволит целевому микроконтроллеру (если он, конечно, не завис наглухо) попасть в обработчик аварийного прерывания, в котором он может успеть перевести внешнее оборудование в безопасный режим.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
К WDT этого типа можно также отнести одновибраторы, собранные на дискретных компонентах (иногда с использованием компараторов). Такие WDT наименее всего подвержены помехам, но они требуют много места на плате, не очень экономичны в энергопотреблении и более сложны в настройке и отладке.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Внешний WDT в виде устройства&quot; [18116-23695] --&gt;
&lt;h2&gt;&lt;a name=&quot;предпосылки_к_использованию&quot; id=&quot;предпосылки_к_использованию&quot;&gt;Предпосылки к использованию&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

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

&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; допущенная программистом ошибка;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; ошибка компилятора;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; ошибка конкретной ревизии кристалла;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; сбой программатора (когда какая-либо ячейка может получить недозаряд);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; износ энергонезависимой памяти программы;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; внутреннее механическое повреждение кристалла (после резких перепадов температур или механических ударов);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; сильные ЭМ-помехи;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; радиационное излучение;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; и пр.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;em&gt;(Детально о причинах отказов ПО см. &amp;quot;2. Причины и последствия сбоев&amp;quot;)&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
Следует обратить внимание, что многие факторы не зависят ни от программистов, ни от схемотехников, ни от ОТК.
&lt;/p&gt;

&lt;p&gt;
Важное замечание: сбой всегда предпочтительнее обнаружить средствами самой программы (это достигается введением в логику программы обработки сигнатур, проверки данных, проверки рассчетов и пр.), т.к. в этом случае есть возможность безопасного восстановления работоспособности, даже если придется для этого произвести программный сброс контроллера, перед которым имеется возможность перевести все внешнее оборудование в безопасный или нейтральный режим. 
&lt;/p&gt;

&lt;p&gt;
Но, во-первых, средствами программы всего проверить и перепроверить нельзя. И ограничения здесь не только в памяти, используемой для тестов, и времени, которое тесты отнимут, но и в том, что сами тесты, являясь частью программы, нужно бы проверять. А потом проверять и эти проверщики и т.д. Поэтому run-time тесты всегда неполные. Во-вторых, существуют непредвиденные сбои, при которых программный счетчик может прыгнуть в такую точку программы, в которой при текущем состоянии ячеек RAM-памяти и текущих настройках периферии, можно задержаться очень надолго. Например, сбоем программного счетчика, программа попала в код передачи байта в программной реализации интерфейса SPI:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; spi_send_byte &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; Data&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt; i;
&amp;nbsp;
        i &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;8&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;----&lt;/span&gt; Сюда попали из&lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt;за сбоя PC
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
            &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Data &lt;span class=&quot;sy3&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x80&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; PIN_DO &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
            &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;             PIN_DO &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
&amp;nbsp;
            spi_delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
            PIN_CLK &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
            spi_delay&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
            PIN_CLK &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
&amp;nbsp;
            Data &lt;span class=&quot;sy1&quot;&gt;&amp;lt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy2&quot;&gt;--&lt;/span&gt;i&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
На момент сбоя в ячейке памяти, где располагается переменная i, могло находиться любое значение, и безобидный цикл на 8 проходов может превратиться в цикл на 50000 проходов. В общем случае от подобных сбоев может спасти только WDT. В предыдущем параграфе (5.3 run-time контроль) было сказано о контроле стека и сигнатурах, которые могли бы сигнализировать о том, что что-то идет не так, но подобные тесты, во-первых, нецелесообразно применять во всех функциях без исключения, а во-вторых, при большом значении числа в ячейках памяти, занятых переменной i, WDT может успеть переполниться раньше, чем программа дойдет до проверок в конце функции.
&lt;/p&gt;

&lt;p&gt;
Два слова стоит сказать об ошибках программы, которые могут привести к зависанию. Само собой разумеется, что надо стремиться к написанию не зависающих программ, уделяя внимание не только качественной проработке проектируемого алгоритма, но также и качественному кодированию, и качественному тестированию. Однако, существует несколько факторов, которые по отдельности или все вмести затрудняют выполнение какого-либо этапа. Например, не всегда заранее известен точный алгоритм будущего устройства (для инновационных проектов), когда изменения в алгоритм вносятся на этапе разработки. Такие изменения могут быть продиктованы различными обстоятельствами, начиная маркетинговыми исследованиями и заканчивая статусом фирм-производителей компонентов (банкротство, перепродажа и пр.). Алгоритм в результате внесения изменений в него может оказаться несогласованным.
&lt;/p&gt;

&lt;p&gt;
Также повлиять на качественное выполнение какого-либо этапа разработки могут факторы организационного характера: над программой трудятся несколько человек, каждый из которых может по-своему интерпретировать какие-либо моменты, описанные в ТЗ, или по-своему додумать то, что в ТЗ не указано явно; программа или ее часть может быть поручена программисту, не имеющему достаточной квалификации или достаточного опыта для качественной реализации. Не следует забывать о факторе времени, выделенного на разработку. Программы, имеющие большое количество состояний, режимов работы и параллельных процессов, не могут быть протестированы целиком, т.к. время тестирования может превысить экономически целесообразные сроки выпуска устройства; поэтому к конечному потребителю программа может попасть, грубо говоря, недоотлаженной. Это не означает, что она будет сбоить и виснуть на каждом шагу, просто при совокупности внештатных ситуаций и стечении каких-то обстоятельств, она может зависнуть. 
&lt;/p&gt;

&lt;p&gt;
Как уже было сказано, нельзя исключать инструментальные ошибки, т.е. ошибки компиляторов, программаторов, наконец, самих микроконтроллеров. Они составляют лишь малую толику от ошибок, допускаемых самими разработчиками, но, тем не менее, имеют место быть.
&lt;/p&gt;

&lt;p&gt;
Итак, даже если разработчик уверен в своей аккуратности, методичности и пунктуальности, существуют факторы, которые могут привести к непредвиденным ошибкам в производимом им ПО, вызывающих в том числе и непредусмотренное зависание программы. Поэтому использование сторожевого таймера в качестве системного монитора является обязательным, особенно, когда речь идет об ответственных системах, сбои в которых могут привести к убыткам или нанесению вреда здоровью людей.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Предпосылки к использованию&quot; [23696-34456] --&gt;
&lt;h2&gt;&lt;a name=&quot;порядок_обнуления_wdt&quot; id=&quot;порядок_обнуления_wdt&quot;&gt;Порядок обнуления WDT&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Примечание: в данном параграфе применяюся вызовы псевдо-функций wdt_clear() и soft_reset().
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Предполагаем, что &lt;strong&gt;wdt_clear()&lt;/strong&gt; выполняет необходимую последовательность действий по обнулению счетчика WDT (например, для внешнего WDT она будет инвертировать управляющий выход, для микроконтроллеров AVR это будет последовательная запись 1 и 0 в регистр WDTCR.4, для МК PIC это выполнение инструкции CLRWDT), &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Предполагаем, что &lt;strong&gt;soft_reset()&lt;/strong&gt; производит последовательность действий по сбросу микроконтроллера: где-то это будет выполнение инструкции RESET, где-то вечный цикл с блокировкой прерываний, который даст переполниться WDT, а где-то - прыжок на начальный адрес ROM, где произойдет переинициализация стека (в реальности этим действиям должны предшествовать действия по переводу внешнего оборудования в безопасный режим, но для наглядности пока это опустим).&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Порядок обнуления WDT&quot; [34457-36018] --&gt;
&lt;h3&gt;&lt;a name=&quot;что_такое_порядок_обнуления_wdt&quot; id=&quot;что_такое_порядок_обнуления_wdt&quot;&gt;Что такое порядок обнуления WDT&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Порядок обнуления WDT - это правила, по которым программа выполняет проверки и принимает решение о том, что на данный момент времени она выполняется корректно и, следовательно, можно давать команду WDT на обнуление счетчика. Т.е. счетчик WDT можно обнулять только тогда,  когда программа считает, что все в порядке. Здесь кроются две проблемы:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; для каждой программы понятие &amp;quot;все в порядке&amp;quot; сугубо индивидуально и во многом зависит от того, какие действия она выполняет как в течение всего периода работы, так и в данный момент времени. Поэтому никакого универсального подхода или общего свода правил для определения этого понятия не существует, и каждая программа требует индивидуальной проработки комплекса проверок.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; когда программа считает, что все в порядке, - это вовсе не значит, что все действительно в порядке. Программа не может проверять каждое свое действие в рамках какого-либо процесса, особенно вкупе с текущим его состоянием и уж тем более - вкупе с текущим состоянием параллельных процессов. Поэтому программа содержит какие-то контрольные точки, при прохождении которых она, делая какие-то проверки, принимает решение о соответствии ее поведения ожидаемому. Но в промежутках между этими точками может произойти все что угодно.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Частью таких контрольных точек можно считать описанные в предыдущем параграфе run-time тесты. Но эти тесты призваны уже заметить ошибку, т.е. на месте засечь некорректное поведение программы, из-за которого нужно либо попытаться восстановить работоспособность программы, либо произвести сброс, либо перейти в безопасный режим. Срабатывание run-time теста однозначно нам говорит о том, что далее выполнять программу нельзя. Однако, надо учесть, что и успешное прохождени конкретного теста не говорит о правильноси функционирования программы в целом. Также контрольными точками могут считаться факты выполнения конкретных участков кода, т.е. те факты, что программа дошла до какого-то места с правильными параметрами. В некоторых случаях приходится учитывать еще и последовательность выполнения таких участков во времени. Следует добавить, что существуют участки кода, в течение выполнения которых нельзя точно сказать, корректно все выполняется или нет; типичный пример - библиотечные функции (см. ниже). 
&lt;/p&gt;

&lt;p&gt;
Умение программиста создавать набор контрольных точек, способных заметить наибольшее число отклонений в поведении программы, акцентируя внимание на ответственных участках, во многом определяет насколько WDT окажется полезным в обнаружении сбоя для конкретной программы. 
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Что такое порядок обнуления WDT&quot; [36019-40689] --&gt;
&lt;h3&gt;&lt;a name=&quot;часто_совершаемая_ошибка_-_безусловное_обнуление&quot; id=&quot;часто_совершаемая_ошибка_-_безусловное_обнуление&quot;&gt;Часто совершаемая ошибка - безусловное обнуление&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Довольно часто можно встретить такой подход, при котором счетчик WDT безусловно обнуляется в главном цикле программы, в прерывании по таймеру, возникающему раз в миллисекунду, или несколько раз в программе с интервалом в 5-10 инструкций (или операторов). Другими словами, программист делает все, чтобы сторожевой таймер не успел переполниться ни при каких условиях. Доходит даже до комичного кода:
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;    &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; uart_getch &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;USART_RX&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; wdt_clear&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;return&lt;/span&gt; USART_RXREG;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Думаю, не надо пояснять, чем этот код плох. Достаточно предположить, что в эту функцию мы можем попасть при отключенном модуле USART, в результате чего повиснем на все время жизни программы.
&lt;/p&gt;

&lt;p&gt;
Такие подходы эквивалентны неиспользованию WDT с одной разницей: у программиста появляется иллюзия по поводу отказоустойчивости программы и, следовательно, сильно снижается бдительность в предвидении возможных причин отказов (что там думать? если что - сторожевой таймер спасет!). Ведь, например, при обнулении WDT  в прерывании, программа может просто повиснуть в вечном цикле ожидая условие, которое никогда не выполнится, в то время как прерывания будут исправно выполняться. Или для систем с одним вектором прерывания (например, младшие семейства PIC-контроллеров) может получиться так, что по случайности будет разрешено прерывание, которое не предусмотрено обработчиком, в результате чего программа никогда не выйдет из обработчика (сразу же при выходе опять произойдет вход, т.к. сброс флага не был предусмотрен), а обработчик таймера, в котором производится сброс WDT, по-прежнему будет выполняться с заданным периодом. Основня программа в этом случае управление уже не получит.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Часто совершаемая ошибка - безусловное обнуление&quot; [40690-43781] --&gt;
&lt;h3&gt;&lt;a name=&quot;периодическое_обнуление_с_проверкой_флагов&quot; id=&quot;периодическое_обнуление_с_проверкой_флагов&quot;&gt;Периодическое обнуление с проверкой флагов&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Часто можно встретить такую рекомендацию (в литературе, в интернет-статьях, на технических форумах): основные функции программы должны после своего выполнения устанавливать флажок, подтверждающий, что функция выполнена; в главный цикл в main() или в прерывание добавляется код, который проверяет, что все нужные функции отработали, и только после этого обнуляет WDT. 
&lt;/p&gt;

&lt;p&gt;
Для этого в программе заводится переменная, содержащая по одному биту на каждую критическую функцию:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;union&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;                    &lt;span class=&quot;co1&quot;&gt;// Каждый бит обнуляется в соответствующей подпрограмме&lt;/span&gt;
        UINT8   bKeyboard &lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;  &lt;span class=&quot;co1&quot;&gt;// Обработка кнопок&lt;/span&gt;
        UINT8   bLCD      &lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;  &lt;span class=&quot;co1&quot;&gt;// Ввывод данных на экран&lt;/span&gt;
        UINT8   bRelay    &lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;  &lt;span class=&quot;co1&quot;&gt;// Включение/выключение реле&lt;/span&gt;
        UINT8   bClock    &lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;  &lt;span class=&quot;co1&quot;&gt;// Часы&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; Bits;
&amp;nbsp;
    UINT8 NotDone;              &lt;span class=&quot;co1&quot;&gt;// Когда все биты сброшены, эта переменная = 0&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; g_WDT;&lt;/pre&gt;
&lt;p&gt;
В главном цикле main() (или в обработчике периодического перрывания) вставляется код проверки флагов:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; main &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    g_WDT.&lt;span class=&quot;me1&quot;&gt;NotDone&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;&lt;span class=&quot;co1&quot;&gt;// При инициалиации считаем, что программа выполняется корректно&lt;/span&gt;
    ...
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;         &lt;span class=&quot;co1&quot;&gt;// Основной цикл программы&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;g_WDT.&lt;span class=&quot;me1&quot;&gt;NotDone&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;   &lt;span class=&quot;co1&quot;&gt;// Первый проход цикла или все подпрограммы отработали&lt;/span&gt;
        &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
            &lt;span class=&quot;co1&quot;&gt;// Устанавливаем флаги функций, требуемых к выполнению&lt;/span&gt;
            g_WDT.&lt;span class=&quot;me1&quot;&gt;Bits&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;bKeyboard&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
            g_WDT.&lt;span class=&quot;me1&quot;&gt;Bits&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;bLCD&lt;/span&gt;      &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
            g_WDT.&lt;span class=&quot;me1&quot;&gt;Bits&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;bRelay&lt;/span&gt;    &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
            g_WDT.&lt;span class=&quot;me1&quot;&gt;Bits&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;bClock&lt;/span&gt;    &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;;
&amp;nbsp;
            &lt;span class=&quot;co1&quot;&gt;// Обнуляем счетчик WDT&lt;/span&gt;
            wdt_clear&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
        KeyboardWork&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        LCDWork&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        RelayWork&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        ClockWork&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Каждая критическая функция сбрасывает соответствующий ей бит, если она выполнилась корректно, например:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; KeyboardWork &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;KeyboardTimeout&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;return&lt;/span&gt;;    &lt;span class=&quot;co1&quot;&gt;// Обрабатываем кнопки с определенным периодом. Если время еще не&lt;/span&gt;
                                     &lt;span class=&quot;co1&quot;&gt;// настало, то выходим из функции (примечание: данный флаг может&lt;/span&gt;
                                     &lt;span class=&quot;co1&quot;&gt;// устанавлиается, например, в прерывании по таймеру&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;co1&quot;&gt;// Читаем кнопки и обрабатываем дребезг&lt;/span&gt;
    ...
&amp;nbsp;
    &lt;span class=&quot;me1&quot;&gt;g_WDT&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;Bits&lt;/span&gt;.&lt;span class=&quot;me1&quot;&gt;bKeyboard&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;  &lt;span class=&quot;co1&quot;&gt;// Обнуляем флаг, сообщая, что функция отработала успешно&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;em&gt;Примечание: флаги можно заводить не только для тех функций, которые вызываются из основного цикла main(), но иногда и для вложенных функций и даже для обработчиков прерываний. Коду проверки важно знать, что ответственные участки кода были выполнены.&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
Для простых приложений такой подход можно считать очень удачным, т.к. он имеет очевидные преимущества:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; он прост и легок в проектировании;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; он легко контролируется, т.к. программа содержит единственное место, где производится обнуление;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; он практически не требует ресурсов (ни ROM, ни RAM, ни времени).&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Однако он имеет и свои недостатки: 
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; все основные функции и ответсвенные участки кода обязаны выполняться даже тогда, когда это не нужно; &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; появляется ограничение на время выполнения всех функций: оно не должно превышать периода переполнения сторожевого таймера. Это заставляет нас неоправданно увеличивать этот период (о недостатке больших периодов будет сказано ниже); &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; сводятся на нет преимущества оконного сторожевого таймера, ведь одна функция из-за сбоя может выполниться мгновенно (из-за ложного перехода, или из-за непредусмотренного состояния управляющих переменных программы), а время выполнения других функций компенсируют этот сбой;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; не учитывается последовательность выполнения функций (иногда она важна);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; время реакции на сбой может достигать времени периода переполнения счетчика WDT;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; наконец, при таком подходе мы не имеем возможности при возникновении нештатной ситуации произвести сброс.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Периодическое обнуление с проверкой флагов&quot; [43782-49934] --&gt;
&lt;h3&gt;&lt;a name=&quot;обнуление_с_контролем_отдельных_функций&quot; id=&quot;обнуление_с_контролем_отдельных_функций&quot;&gt;Обнуление с контролем отдельных функций&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Также часто встречается рекомендация в вызываемой функции выставлять переменной конкретное значение, а после выхода проверять его и, если оно соответствует ожидаемому, производить обнуление WDT:
&lt;/p&gt;
&lt;pre class=&quot;code cpp&quot;&gt;    uint16 g_WDT_Temp; &lt;span class=&quot;co1&quot;&gt;// глобальная переменная&lt;/span&gt;
&amp;nbsp;
    ...
    &lt;span class=&quot;me1&quot;&gt;g_WDT_Temp&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
    process_func1&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;   &lt;span class=&quot;co1&quot;&gt;// В теле этой функции к WDT_Temp прибавляется 0x1111&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;g_WDT_Temp &lt;span class=&quot;sy1&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x1111&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; wdt_clear&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;; 
    &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt; soft_reset&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    process_func2&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;   &lt;span class=&quot;co1&quot;&gt;// В теле этой функции к WDT_Temp прибавляется 0x2222&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;g_WDT_Temp &lt;span class=&quot;sy1&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x3333&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; wdt_clear&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt; soft_reset&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    process_func3&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;   &lt;span class=&quot;co1&quot;&gt;// В теле этой функции к WDT_Temp прибавляется 0x3333&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;g_WDT_Temp &lt;span class=&quot;sy1&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x6666&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; wdt_clear&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt; soft_reset&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    ...&lt;/pre&gt;
&lt;p&gt;
Метод является комбинацией с одним из видов run-time проверок. Если мы случайно пропускаем выполнение какой-либо функции (например из-за сбоя PC), то run-time проверка сигнатуры функции вызовет обработчик soft_reset(). Вне комбинации с run-time проверками этот метод не только малоэффективен, но даже опасен, т.к. если убрать во всех условиях ветку else, то даже после обнаружения сбоя (т.е. сбой не только произошел, но уже замечен) мы позволим программе выполнить еще несколько функций, уже находясь во внештатном режиме.
&lt;/p&gt;

&lt;p&gt;
Т.е. данный метод является дополнением к run-time проверкам. На мой взгляд, он не очень эффективен, т.к.:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; сильно портит наглядность программы, даже если вынести условие в отдельную функцию; &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; при изменении логики работы программы потребуется вносить коррективы в константы; &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; довольно прожорлив по ресурсам; &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; практически бесполезен при использовании внешних библиотек.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Однако он имеет два преимущества: 
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt;  быстрая реакция на сбой при использовании оконного сторожевого таймера. Например, функция process_func1() отработала, выставила сигнатуру, но из-за сбоя выполнилась не за 5 мс, как предполагалось, а за 100 мкс. И тогда даже при правильно установленной сигнатуре реакция на сбой будет моментальная, что не позволит выполняться остальным функциям уже при наличии сбоя.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; возможность использования при низкой тактовой частоте, когда скорость программы мала и время переполнения сторожевого таймера близко ко времени выполнения несложной функции (это особенно актуально при обработке исключения по срыву генерации).&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Обнуление с контролем отдельных функций&quot; [49935-53809] --&gt;
&lt;h3&gt;&lt;a name=&quot;периодическое_обнуление_с_контролем_состояния&quot; id=&quot;периодическое_обнуление_с_контролем_состояния&quot;&gt;Периодическое обнуление с контролем состояния&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Суть метода заключается в том, что обработчик WDT, анализируя специально заведенные для этих целей глобальные переменные, знает, в каком режиме работает программа, что она должна делать и чего она делать не должна. Вызов wdt_clear() производится при прохождении всех проверок внутри обработчика периодического прерывания (чаще всего от таймера). Это позволяет нам вести постоянный контроль за состоянием программы с определенным периодом вне зависимости от времени выполнения отдельных функций. В сущности этот метод является расширенной версией метода обнуления с контролем флагов за исключением того, что он только в редких случаях может использоваться вне прерывания.
&lt;/p&gt;

&lt;p&gt;
Метод хорош тем, что позволяет выполнять различные проверки для различных режимов работы, т.е. позволяет контролировать правильность работы программы в целом (например, для диктофона не могут быть одновременно активны режимы записи и воспроизведения; автосигнализация не может запоминать новые серийные номера брелоков, если она не в режиме программирования и т.п.). В зависимости от текущего режима работы и выпоняемых в данный момент действий, порядок и набор проверок может меняться. К примеру, электрическая плита может работать в 3х режимах: режим сна, режим готовки и режим настройки. В каждом из них выполняются разные наборы функций, которые было бы неудобно контролировать скопом, поэтому для контроля каждого набора создается своя ветвь проверок в зависимости от режима.
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;int&lt;/span&gt; g_Mode &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; TIMER1_ISR &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;bool&lt;/span&gt; bAllowClearWDT;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;g_Mode&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;case&lt;/span&gt; MODE_IDLE&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt;
             ...
             &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;case&lt;/span&gt; MODE_COOKING&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt;
             ...
             &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;case&lt;/span&gt; MODE_SETUP&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt;
             ...
             &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;
&amp;nbsp;
        &lt;span class=&quot;kw1&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;sy4&quot;&gt;:&lt;/span&gt;            &lt;span class=&quot;co1&quot;&gt;// Несуществующий режим&lt;/span&gt;
             &lt;span class=&quot;co1&quot;&gt;// Выполняем какие-то действия по оживлению устройства, если это&lt;/span&gt;
             &lt;span class=&quot;co1&quot;&gt;// возможно, или производим программный сброс, предварительно&lt;/span&gt;
             &lt;span class=&quot;co1&quot;&gt;// запротоколировав сбой&lt;/span&gt;
             &lt;span class=&quot;kw1&quot;&gt;break&lt;/span&gt;;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;bAllowClearWDT&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; wdt_clear&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    ...
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Этот метод лишен основных недостатков двух предыдущих методов обнуления (с контролем флагов и контролем функций), а именно:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; он позволяет функциям выполняться настолько долго, насколько это требуется;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; он отслеживает выполнение только тех функций, которые должны выполняться в данный момент;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; может учитывать последовательность выполнения функций;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; есть возможность произвести обработку внештатной ситуации вручную (в худшем случае, произведя сброс).&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Но главное преимущество этого метода в том, что он может быть применен тогда, когда программа использует внешние библиотеки, учитывая, что в них нет четкого регламента по времени выполнения отдельных функций, а также при работе программы под управлением RTOS, где каждый из нескольких процессов живет своей жизнью (см. ниже).
&lt;/p&gt;

&lt;p&gt;
Однако, при своей универсальности этот метод не лишен недостатков:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; он требует тщательной проработки алгоритма;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; он требует свободных ресурсов RAM (для переменных состояния) и ROM (для кода проверки);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; скорость реакции на сбой - один период WDT, т.е. нельзя применять с оконными WDT;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; при неаккуратном программировании время кода проверок всех условий может оказаться затянутым.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Периодическое обнуление с контролем состояния&quot; [53810-59469] --&gt;
&lt;h3&gt;&lt;a name=&quot;комбинированный_метод&quot; id=&quot;комбинированный_метод&quot;&gt;Комбинированный метод&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Иногда бывает удобно выхватить из каждого метода что-нибудь одно и использовать в комбинации с другим. Например, может понадобится написать функцию (скажем, автомат состояний), для которой сам алгоритм проверки состояний превратится в сложный ветвящийся комплекс операторов ветвлений и циклов. В таком случае может оказаться целесообразным не учитывать эту функцию в проверках внутри прерывания, а воспользоваться для нее методом обнуления с контролем функций или обнулением в цикле, т.е. в критических узлах функции расставить вызовы wdt_clear() (обязательно с проверкой текущего состояния). Это может усложнить анализ причин сброса, но иногда намного упрощает проработку алгоритма контроля выполнения программы.
&lt;/p&gt;

&lt;p&gt;

&lt;p&gt;&lt;div class=&quot;noteclassic&quot;&gt;
При выборе любого метода обнуления WDT нужно помнить главные правила:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; нельзя обнулять WDT безусловно;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; проверки, определяющие, нужно ли обнулять WDT, должны производиться максимально быстро, чтобы не перегружать ядро микроконтроллера действиями, не относящимися к рабочей функции программы;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; те сбои, которые можно обнаружить программно, следует обнаружать программно и реагировать на них адекватно; WDT - последний рубеж.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

&lt;/div&gt;&lt;/p&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Комбинированный метод&quot; [59470-61627] --&gt;
&lt;h2&gt;&lt;a name=&quot;выбор_интервала&quot; id=&quot;выбор_интервала&quot;&gt;Выбор интервала&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

При проектировании алгоритма обнуления WDT перед прораммистом встает вопрос выбора интревала WDT. Диапазон временных интервалов, проедоставляемый сторожевыми таймерами разных производителей, а также внутренним WDT, довольно широк: от единиц миллисекунд до единиц секунд (для внешних) или до нескольких суток (для втутренних). Интревал следует выбирать исходя из возлагаемых на WDT задач. Например, если мы планируем использовать WDT только для вывода МК из зависания, то редко могут понадобиться интервалы длиннее секунды-двух. Если же WDT будет использован для периодической активации контроллера, работающего в режиме пониженного энергопотребления, то тут, в зависимости от конкретной задачи, могут быть уместны интервалы в несколько часов или даже суток. В некоторых случаях целесообразно изменять интервал в ходе работы: например, его можно делать короче при выполнении ответственных функций или, наоборот, делать длиннее при переходе в режим пониженного энергопотребления.
&lt;/p&gt;

&lt;p&gt;
Интервалы, касающиеся специфичных приемнений WDT (об этом ниже), выбираются по-своему в каждом случае, и тут нужно руководствоваться особенностями конкретного применения. То же самое касается интервала для SLEEP-режима: в зависимости от области применения может быть выбран различный интервал. А вот выбирая интервал для использования WDT в качестве монитора прораммы (т.е. фактически с точки зрения программы - интервала обработки процедуры обнуления WDT), следует руководствоваться тремя основными критериями:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; рекция на сбой должна быть максимально быстрой;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; время всех предварительных проверок для обнуления WDT должно быть несоизмеримо мало по сравнению с периодом;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; время выполнения участков кода, в течение которых принципиально невозможно осуществить процедуру обнуления WDT, должно быть намного меньше интервала обработки кода обнуления счетчика WDT.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;максимальная_скорость_реакции&quot; id=&quot;максимальная_скорость_реакции&quot;&gt;Максимальная скорость реакции&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Чем быстрее произойдет сброс контроллера после возникновения сбоя, тем лучше. Это и понятно, раз произошел сбой и программа начала выполняться по внештатному расписанию, т.е. перестала соответствовать спецификации, то нужно это прекратить как можно быстрее. Например, для программы, контролирующей целостность настила ступеней движущегося эскалатора, непозволительно находиться в зависшем состоянии в течение времени, соизмеримого со временем прохода одной ступени над датчиком. 
&lt;/p&gt;

&lt;p&gt;
Кроме того, нужно помнить, что зависшая программа - это далеко не всегда программа, которая ничего не делает. Бывает, конечно, что собьестя генерация кварца тактирующего микроконтроллер. Но в большинстве случаев программа продожает выполняться, причем она может не просто зациклиться на каком-то небольшом участке (по каким-то причинам не выполняется условие выхода из цикла), но у нее может сбиться указатель стека, в результате чего после выполнения первой же встретившейся инструкции RETURN программа продолжит выполнение с произвольного адреса (в стеке могли находиться данные, которые после сбоя указателя стека ошибочно были восприняты как адрес возврата). 
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;время_выполнения_проверок&quot; id=&quot;время_выполнения_проверок&quot;&gt;Время выполнения проверок&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Если программа является многопоточным приложением, многие процессы в которой живут большей частью собственной жизнью, то код проверок правильности состояния программы может оказаться сложным и выполняться довольно длительное время (в худшем случае может достигать десятков и сотен микросекунд). Становится очевидным, что если делать эти проверки сравнительно часто, то снизится эффективность самой программы. И не только из-за того, что 1% или 10% времени будет уходить на проверки, а еще и из-за того, что проверка состояния программы нарушает время ее реакции на какое-либо событие (особенно, если проверка находится в обработчике прерывания высокого уровня).
&lt;/p&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;критические_участки_кода&quot; id=&quot;критические_участки_кода&quot;&gt;Критические участки кода&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Программа может содержать участки кода, критичные к выполнению с точностью до такта, где инструкции для обнуления счетчика сторожеваого таймера будет просто некуда втиснуть. Сами инструкции обнуления вставить, возможно, удастся, но на код проверки правильности состояния программы может уже не остаться места, а без этого кода WDT может сослужить плохую службу (мы уже говорили о зависании в цикле с безусловным обнулением WDT). Таким кодом может оказаться, например, обработка какого-то внешнего синала, требующая точной выборки по времени. Проблема может быть частично решена применением DMA-модуля, если он есть. Но в общем случае надо учитывать наличие таких фрагментов кода при выборе интервала обнуления WDT.
&lt;/p&gt;

&lt;p&gt;
Также стоит отметить, что некоторые контроллеры имеют возможность переключаться на резервный генератор при срыве генерации основного. Обычно резервный генератор имеет частоту на два-три порядка ниже частоты основного генератора (часто 32768 Гц). Обычно при переходе на резервный генератор генерируется немаскируемое прерывание, которое позволяет нам выполнить код по переводу устройства в резервный режим перед выполнением сброса. Но следует учесть, что этот код будет выполняться в сотни или тысячи раз медленнее, чем при тактировании от основного генератора, а тактирование сторожевого таймера при этом останется без изменений. И при неправильном выборе интервала сторожевого таймера или при неучете особенностей выполнения кода при тактировании от резервного генератора может получиться ситуация, в которой счетчик WDT успеет переполниться до того, как будут выполнены все действия по подготовке к сбросу. Т.е. в обработчике немаскируемого прерывания, возникающего при срыве генерации основного генератора, следует применять обнуление WDT по особым правилам. Разумеется, в таком случае контролировать правильность программы уже не имеет смысла, но кое-какие проверки перед обнулением WDT выполнять, скорее всего, придется. В данном случае можно применять метод обнуления с контролем отдельных функций.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Выбор интервала&quot; [61628-72227] --&gt;
&lt;h2&gt;&lt;a name=&quot;работа_с_библиотечными_функциями&quot; id=&quot;работа_с_библиотечными_функциями&quot;&gt;Работа с библиотечными функциями&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Отдельно стоит вопрос порядка обнуления сторожевого таймера, когда в программе используются библиотеки сторонних производителей. Сами библиотечные функции никогда не производят обнуление WDT, с другой стороны в общем случае неизвестно, сколько времени может потребоваться на выполнение какой-либо библиотечной функции. Простая строка:
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;    printf(&quot; %5.3f\n&quot;, sin(t));&lt;/pre&gt;
&lt;p&gt;
может выполняться многие тысячи тактов, и даже обрамление ее вызовами wdt_clear():
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;    wdt_clear();
    printf(&quot; %5.3f\n&quot;, sin(t));
    wdt_clear();&lt;/pre&gt;
&lt;p&gt;
не всегда может спасти от переполнения WDT. Выхода здесь два:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; увеличивать интервал сторожевого таймера;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; обнулять WDT в прерывании (с контролем состояния).&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;strong&gt;Увеличение интервала&lt;/strong&gt; - довольно сомнительное решение в данном случае. Тому есть несколько причин: 
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Интервал WDT в таком случае берется совершенно неподходящим к конкретной задаче. Причем надо помнить, что мы не можем просто взять такой интервал, при котором библиотечная функция успеет выполниться (помним, что в общем случае мы не знаем времени ее выполения при различных условиях). Нам приходится брать такой интервал, в течение которого мы можем позволить этой функции выполняться, т.е. с запасом, иногда не оправданно затянутым. В результате чего мы теряем в скорости реакции на сбой.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Если мы, даже взяв интервал с запасом, все же ошибемся (в одной из миллиона комбинаций параметров функции время ее выполнения затянется), то произойдет неконтролируемый сброс контроллера, т.е. такой сброс, при котором у нас нет возможности перевести внешнее оборудование в безопасный режим.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

&lt;strong&gt;Обнуление в прерывании с контролем состояния&lt;/strong&gt; намного лучше подходит для решения этой задачи. Такой подход позволяет нам учесть длительное время выполнения быблиотечной функции, не увеличивая при этом интервал самого сторожевого таймера. Для этого нужно всего лишь завести глобальную переменную, в которую перед вызовом библиотечной функции можно записывать предполагаемое время ее выполнения с каким угодно запасом. А в обработчике прерывания к коду проверок состояния программы добавить обработку этой переменной.
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;struct&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    UINT8    ucActive;
    INT16    nCounter;
&amp;nbsp;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt; g_WDT_Library;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; TIMER1_ISR &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;bool&lt;/span&gt; bAllowClearWDT;
    ...
    &lt;span class=&quot;me1&quot;&gt;bAllowClearWDT&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;true&lt;/span&gt;;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;g_WDT_Library.&lt;span class=&quot;me1&quot;&gt;ucActive&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;==&lt;/span&gt;  &lt;span class=&quot;nu12&quot;&gt;0x55&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;       &lt;span class=&quot;co1&quot;&gt;// Проверяем, не выполнятеся ли сейчас библиотечная функция&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;g_WDT_Library.&lt;span class=&quot;me1&quot;&gt;nCounter&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
            bAllowClearWDT &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kw2&quot;&gt;false&lt;/span&gt;;
        &lt;span class=&quot;kw1&quot;&gt;else&lt;/span&gt;
            g_WDT_Library.&lt;span class=&quot;me1&quot;&gt;nCounter&lt;/span&gt;--;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
    ...
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;bAllowClearWDT&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; wdt_clear&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    ...
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Перед вызовом библиотечных функций, время выполнения которых вывает сомнения, переменная g_WDT_Library.nCounter устанавливается в соответствии с ожидаемым временем выполнения (это удобно организовать в виде функции, т.к. могут потребоваться действия для обеспечения атомарного доступа к счетчику):
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; wdt_lib_start &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;UINT16 value&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    g_WDT_Library.&lt;span class=&quot;me1&quot;&gt;ucActive&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0xAA&lt;/span&gt;;    &lt;span class=&quot;co1&quot;&gt;// Блокируем моификацию счетчика в прервании (любое число != 0x55)&lt;/span&gt;
    g_WDT_Library.&lt;span class=&quot;me1&quot;&gt;nCounter&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; value;
    g_WDT_Library.&lt;span class=&quot;me1&quot;&gt;ucActive&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0x55&lt;/span&gt;;    &lt;span class=&quot;co1&quot;&gt;// Запускаем отсчет&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; wdt_lib_stop &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;UINT16 value&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    g_WDT_Library.&lt;span class=&quot;me1&quot;&gt;ucActive&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu12&quot;&gt;0xAA&lt;/span&gt;;   &lt;span class=&quot;co1&quot;&gt;// останавливаем контроль WDT для библиотечной функции&lt;/span&gt;
                                     &lt;span class=&quot;co1&quot;&gt;// Пишем любое число != 0x55&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; main &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    ...
    &lt;span class=&quot;kw1&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        ...
        &lt;span class=&quot;me1&quot;&gt;wdt_lib_start&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;            &lt;span class=&quot;co1&quot;&gt;// Выделяем 100 мс на выполнение функции&lt;/span&gt;
        &lt;span class=&quot;kw3&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;st0&quot;&gt;&amp;quot; %5.3f&lt;span class=&quot;es0&quot;&gt;\n&lt;/span&gt;&amp;quot;&lt;/span&gt;, &lt;span class=&quot;kw3&quot;&gt;sin&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;t&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        wdt_lib_stop&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
        ...
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Работа с библиотечными функциями&quot; [72228-77979] --&gt;
&lt;h2&gt;&lt;a name=&quot;работа_в_программе_под_управлением_rtos&quot; id=&quot;работа_в_программе_под_управлением_rtos&quot;&gt;Работа в программе под управлением RTOS&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

При работе программы под управлением RTOS порядок обнуления WDT несколько усложняется, из-за того, что потребуется следить сразу за несколькими процессами (задачами). Построение полноценного алгоритма обнуления WDT в случае использования RTOS - это совсем нетривиальная задача; сложности, с которыми мы сталкиваемся приведены ниже:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Задачи подолгу могут находиться в режиме ожидания, не получая управления. В таких случаях факт невыполнения задачи в течение какого-то времени не может быть интерпретирован однозначно: мы не знаем, находится ли задача в ожидании или она была удалена или заблокирована по ошибке, или ждет сигнала от другой задачи, которая повисла.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; В программе появляется довольно много ответственных переменных, которые для программы неподконтрольны, и мы не можем гарантировать их целостность (помним, что сильная помеха может изменить состояние любого триггера): дескрипторы задач (например, мы не можем контролировать, правильно ли восстановился указатель стека), сообщения (сообщение может оказаться активным, но указывать вникуда), очереди и прочие объекты RTOS. &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Задачи редко живут своей жизнью, обычно они как-то синхронизированы с другими задачами, и, пытаясь установить факт правильности выполнения программы, мы должны проверять не только состояние процесса, но и соответствие его состоянию параллельных процессов. &lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
 
Ввиду этих сложностей обычно ограничиваются построением простых индивидуальных тестов с контролем состояний для каждой активной задачи и выполняют обнуление при успешном прохождении их всех. Т.е. своего рода надстройка над этими тестами в виде обнуления с проверкой флагов. Причем в случае с RTOS эти проверки можно делать только в прерывании высокого приоритета (выше системного). Все более детальные проверки лучше реализовывать в виде run-time тестов. Это, во-первых, очень разгрузит код проверки условий обнуления WDT, а во-вторых, как уже было сказано, что сбой всегда предпочтительнее заметить на программном уровне, чтобы иметь возможность попытаться восстановить работоспособность или, в крайнем случае, произвести сброс безопасно, предварительно переведя все внешнее оборудование в безопасный режим. А сторожевому таймеру оставить функцию сброса только в самых критических ситуациях.
&lt;/p&gt;

&lt;p&gt;
Но, даже реализуя проверки по упрощенной схеме, не стоит забывать о некоторых особенностях, превносимых RTOS:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Т.к. все задачи делят одно ядро, то и время выполнения отдельных функций может быть затянуто из-за того, что задача может быть вытеснена более приоритетной, причем иногда - на неопределенное время.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Все ожидания событий в задачах должны делаться только с аварийным выходом по таймауту. Это позволит не только упоярдочить проверку правильности выполнения задачи кодом управления WDT, но и сделает предсказуемым поведение программы для run-time тестов.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Не все задачи постоянно активны: некоторые еще не созданы, некоторые уже удалены, некоторые - в глубоком сне. Это должно быть учтено проверками, например, в каждой структуре, контролирующей ход выполнения задачи, дожен быть выделен отдельный байт для сигнатуры, которая бы говорила коду проверки, что данна задача в проверке не участвует (см. пример работы с библиотечными функциями).&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Работа в программе под управлением RTOS&quot; [77980-83881] --&gt;
&lt;h2&gt;&lt;a name=&quot;тестирование_wdt_при_включении_питания&quot; id=&quot;тестирование_wdt_при_включении_питания&quot;&gt;Тестирование WDT при включении питания&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

В особо ответственных системах WDT как контролирующий узел обязательно должен быть протестирован (иначе полагаться на него нельзя). Такие проверки предписывают делать европейский стандарт IEC 60730 (Annex H 11.12.7 п. 8) или американский стандарт UL1998 (A2.1 п.8). В зависимости от условий работы тестирование может производиться единожды при включении питания или с каким-то периодом, но не нарушая при этом целостность выполнения программы (т.е. в случае периодического тестирования перед проведением теста все внешнее оборудование должно быть переведено в безопасный режим, сохранены в энергонезависимую память данные, которые необходимо восстановить после перезагрузки. 
&lt;/p&gt;

&lt;p&gt;
Более детально тестирование WDT описано в &amp;quot;5.2 Самодиагностика&amp;quot;, здесь повторю только основные принципы проверки:

&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; В оперативной памяти выделяется несколько байт для сигнатуры, отвечающей за стадию проведения теста.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; При рестарте программы в первую очередь проверяется эта сигнатура на соответствие контрольного слова заданному шаблону (примечание: шаблон хранится в ROM). Если оно:&lt;/div&gt;
&lt;ol&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; соответствует шаблону рабочего режима, а причиной сброса был WDT, то переходим к тестированию WDT (п. 3), иначе проверяем причины сброса (п.3). &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; Если причиной сброса был сигнал от WDT, то тест считается успешно пройденным и можно переходить на п.8. Иначе повторяем проверку с п.4 (такая ситуация может случиться, если сброс микроконтроллера произошел по каким-то другим причинам во время проведения теста на WDT).&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Записать шаблон в сигнатуру. &lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Запрограммировать WDT на короткий интервал. Лучше всего, если это будет интервал, используемый в программе. Если в программе используется длительный интервал, то для теста можно взять укороченный, но так, чтобы под контролем оказалось максимальное количество битов счетчика WDT.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Настроить необходимый минимум периферии, чтобы не было висячих выводов.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Повисаем в вечном цикле.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; Настраиваем WDT на рабочий интервал и продолжаем работу. В сигнатуру прописываем шаблон, соответствующий рабочему режиму.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Тестирование WDT при включении питания&quot; [83882-87606] --&gt;
&lt;h2&gt;&lt;a name=&quot;поведение_программы_при_сбросе_от_wdt&quot; id=&quot;поведение_программы_при_сбросе_от_wdt&quot;&gt;Поведение программы при сбросе от WDT&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Основной параграф, описывающий поведение программы при сбросе, это &amp;quot;5.5 Что делать при обнаружении сбоя&amp;quot;. Здесь опишу только основные моменты, касающиеся сброса контроллера, вызванного переполнением WDT. 
&lt;/p&gt;

&lt;p&gt;
Все действия делятся на два этапа:
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; протоколирование;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; восстановление работы.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;

&lt;h4&gt;&lt;a name=&quot;протоколирование&quot; id=&quot;протоколирование&quot;&gt;Протоколирование&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;level4&quot;&gt;

&lt;p&gt;

Установить причины сброса, вызванного переполнением WDT, программными средствами довольно затруднительно. Даже если мы проанаизируем контрольные переменные в памяти (такие переменные должны быть размещены в сегментах RAM, не подвергающихся начальной инициализации или обнулению; для этого в разных компиляторах есть специальные директивы), то в лучшем случае получим только последствия сбоя, т.е. несоответствие значений этих переменных предполагаемым, а саму причину таким способом не установить. Кроме того, все эти переменные могут содержать достоверные значения, т.к. сбой мог произойти из-за срыва генерации, или из-за изменения значения программного счетчика, или из-за изменения триггеров самого WDT (об этом ниже).
&lt;/p&gt;

&lt;p&gt;
Но причины сбоя можно попытаться проанализировать вручную. Для этого программа должна иметь возможность при старте сохранять в энергонезависимую память или передавать по внешним каналам связи набор значимых переменных, регистров, а также участка стека. Все эти данные могут в дальнейшем помочь нам установить в каком месте программы и в каком ее состоянии произошел сбой. Причем по возможности при каждом сбое лучше сохранять все эти данные в разные участки энергонезависимой памяти. Это наберет статистикку сбоев и увеличит наши шансы установить причину, что особенно полезно, если причина была в недостаточно хорошо проработанном алгоритме программы, т.е. если сбой был вызван самой программой, а не внешними причинами. Но даже при невозможности сохранять данные в разные участки энергонезависимой памяти (например, если не позволяет ее свободный объем), то обязательно нужно сохранять счетчик сбоев. При обслуживании устройства по этому счетчику можно будет делать выводы о том, был ли это случайный сбой, или же систематический, что является сигналом к срочной переработке устройства.
&lt;/p&gt;

&lt;p&gt;
Какие данные нужно сохранять:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; все те переменные, которые участвуют в проверках на возможность обнуления, т.е. те, которые являются индикаторами правильности работы программы;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; содержимое стека. Здесь есть три сложности: &lt;/div&gt;
&lt;ol&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; Некоторые компиляторы используют стек для обмена данными между функциями, в результате чего используемая часть стека сильно разрастается, что потребует большего объема энергонезависимой памяти для протоколирования;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; При работе программы под управлением вытесняющей RTOS будет уже несколько стеков, что также требует большего объема энергонезависимой памяти.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level2&quot;&gt;&lt;div class=&quot;li&quot;&gt; некоторые контроллеры имеют аппаратный стек без возможности программного доступа.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; некоторые регистры микроконтроллера остаются неизменными после сбоя, и их тоже есть смысл сохранить;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; счетчик сбоев и причину сброса (в данном случае WDT).&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; если есть возможность, то полезно сохранить дату и время.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Чем больше данных мы сохраним, тем больше возможностей установить причину сбоя и, если это возможно, устранить ее.
&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;
TODO:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt; Восстановление работы &lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt;
&lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt; Зависание и преждевременный сброс WDT &lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt;
&lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt; RC&lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt;генератор и температура &lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt;
&lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt; Другие применения WDT &lt;span class=&quot;sy2&quot;&gt;-&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;

Виктор Тимофеев, сентябрь 2010
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Поведение программы при сбросе от WDT&quot; [87607-] --&gt;</description>
    </item>
    <item rdf:about="http://www.pic24.ru/doku.php/osa/articles/winavr_bug?rev=1276665351">
        <dc:format>text/html</dc:format>
        <dc:date>2010-06-16T09:15:51+03:00</dc:date>
        <title>Ошибка WinAVR</title>
        <link>http://www.pic24.ru/doku.php/osa/articles/winavr_bug?rev=1276665351</link>
        <description>


&lt;h1&gt;&lt;a name=&quot;ошибка_winavr&quot; id=&quot;ошибка_winavr&quot;&gt;Ошибка WinAVR&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;level1&quot;&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Ошибка WinAVR&quot; [1-35] --&gt;
&lt;h2&gt;&lt;a name=&quot;вступление&quot; id=&quot;вступление&quot;&gt;Вступление&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;p&gt;

Компилятор WinAVR GCC один из самых распространенных Си-компиляторов для микроконтроллеров фирмы AVR. Он был первым кандидатом на портирование OSA. В процессе изучения его особенностей (версия 20090313) была найдена ошибка, которая не позволяет полноценно портировать на него кооперативную ОС. Что значит &amp;quot;полноценно&amp;quot;? Это значит, что есть возможность использовать ОС только при отключенной оптимизации (ключ компилятору &amp;quot;-o0&amp;quot;). Это очень неприятно, т.к. при включенной оптимизации генерируется код в полтора и более раза компактнее, чем с выключенной. Кроме того, оптимизированный код получается более быстрым. 
&lt;/p&gt;

&lt;p&gt;
Примечание: &lt;span class=&quot;important&quot;&gt;В OSA версии 100531 найден механизм обхода проблемы&lt;/span&gt;
&lt;/p&gt;

&lt;p&gt;

При наличии такой проблемы встает вопрос целесообразности применения ОС в своем проекте. Мало того, что сама ОС под свои нужды заберет какую-то часть ресурсов (ROM, RAM, время), так еще и остальной код окажется невозможным оптимизировать. При таких условиях результирующий код с использованием ОС и без оптимизации окажется в два раза объемнее, чем без ОС и с оптимизацией. 
&lt;/p&gt;

&lt;p&gt;
По причине невозможности (на сегодняшний день) использовать оптимизацию порт OSA для WinAVR большей частью написан на ассемблере. Это немного успокаивает тем, что хотя бы ядро ОС оптимизировано (на сегодня еще есть что оптимизировать, но со временем я доведу порт до максимальной компактности и скорости).
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Вступление&quot; [36-2509] --&gt;
&lt;h2&gt;&lt;a name=&quot;описание_ошибки&quot; id=&quot;описание_ошибки&quot;&gt;Описание ошибки&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Описание ошибки&quot; [2510-2552] --&gt;
&lt;h3&gt;&lt;a name=&quot;стек_в_winavr&quot; id=&quot;стек_в_winavr&quot;&gt;Стек в WinAVR&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Компилятор WinAVR формирует общий стек для адресов возврата и данных. Когда программа вызывает функцию, использующую локальные переменные, эта функция делает следующее:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; первым делом сохраняет пару регистров r28:r29 (Y-указатель);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; затем выделяет стек для локальных переменных (это делается либо командами push для выделения одного байта, либо call $+1 для выделения сразу двух байт);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; на последнем этапе указатель стека SP копируется в Y-указатель.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

Таким образом получается, что на момент начала работы кода функции указатель стека SP и Y-указатель содержат один и тот же адрес. При дальнейших вызовах (если эта функция будет вызывать другие функции) указатель стека SP будет &amp;quot;расти&amp;quot; вниз, а Y будет выполнять роль указателя фрейма, который располагается &amp;quot;сверху&amp;quot;. Ниже приведен типичный код инициализации фрейма и схематическое представление его размещения в памяти.
&lt;/p&gt;
&lt;pre class=&quot;asm code asm&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co1&quot;&gt;; void func (void)&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;push&lt;/span&gt;    r28          &lt;span class=&quot;sy0&quot;&gt;//&lt;/span&gt; Сохраняем в стеке Y&lt;span class=&quot;sy0&quot;&gt;-&lt;/span&gt;указатель
    &lt;span class=&quot;kw1&quot;&gt;push&lt;/span&gt;    r29
    rcall   $&lt;span class=&quot;sy0&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;          &lt;span class=&quot;sy0&quot;&gt;//&lt;/span&gt; Выделяем фрейм под &lt;span class=&quot;nu0&quot;&gt;5&lt;/span&gt; локальных переменных
    rcall   $&lt;span class=&quot;sy0&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;push&lt;/span&gt;    r17
    &lt;span class=&quot;kw1&quot;&gt;in&lt;/span&gt;      r28&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; SPL     &lt;span class=&quot;sy0&quot;&gt;//&lt;/span&gt; Присваиваем Y = &lt;span class=&quot;kw3&quot;&gt;SP&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;in&lt;/span&gt;      r29&lt;span class=&quot;sy0&quot;&gt;,&lt;/span&gt; SPH
    &lt;span class=&quot;sy0&quot;&gt;...&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/winavr_func_stack.png?id=osa%3Aarticles%3Awinavr_bug&quot; class=&quot;media&quot; title=&quot;osa:articles:winavr_func_stack.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/winavr_func_stack.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Это очень удачная модель использования стека, поскольку допускает повторный вход в функцию (в том числе рекурсию). При каждом вызове функция сама себе будет выделять фрейм, гарантируя тем самым непересекаемость областей локальных переменных для любого пути в графе вызовов.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Стек в WinAVR&quot; [2553-5041] --&gt;
&lt;h3&gt;&lt;a name=&quot;кооперативная_ос_и_локальные_переменные&quot; id=&quot;кооперативная_ос_и_локальные_переменные&quot;&gt;Кооперативная ОС и локальные переменные&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Для обеспечения параллельности выполнения нескольких функций (задач) ОС должна иметь механизм, способный прерывать выполнение функции в середине, передавая управление другим функциям для выполнения, а затем возвращать управление обратно прерванной функции так, чтобы она продолжила свое выполнение с того же места, на котором была прервана. Разумеется, этот же механизм должен заботиться о сохранении контекста, т.е. помимо программного счетчика должны быть сохранены/восстановлены прочие служебные регистры (указатель стека, регистр статуса, набор регистров общего назначения (РОН)) и локальные переменные. Со служебными регистрами и РОН все понятно: их набор определен изначально, и с сохранением/восстановлением сложностей не возникает. А вот с локальными переменными сложнее. 
&lt;/p&gt;

&lt;p&gt;
В вытесняющих ОС под каждую задачу выделяется своя область стека; это гарантирует сохранность всех локальных переменных и всей истории вызовов для текущей задачи. Но на малоресурсных контроллерах вытесняющая ОС не заработает из-за отсутствия достаточного объема оперативной памяти для сохранения контекста задач; для таких контроллеров можно использовать ОС с кооперативным планировщиком. В отличие от вытесняющей ОС, планировщик которой может прервать выполнение задачи в любой момент, в кооперативной ОС переключение задач может произойти только там, где укажет программист. Например:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; x, y;
    lcd_init&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;               &lt;span class=&quot;co1&quot;&gt;// инициализация модуля ЖКИ&lt;/span&gt;
&amp;nbsp;
    OS_Yield&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;               &lt;span class=&quot;co1&quot;&gt;// передача управления другим задачам (фактически - планировщику)&lt;/span&gt;
&amp;nbsp;
    x &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; lcd_getx&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;           &lt;span class=&quot;co1&quot;&gt;// вывод &amp;quot;Hello world&amp;quot; в строке под курсором&lt;/span&gt;
    y &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; lcd_gety&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    lcd_outtext&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;x, y &lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;, &lt;span class=&quot;st0&quot;&gt;&amp;quot;Hello world&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    OS_Yield&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;               &lt;span class=&quot;co1&quot;&gt;// передача управления&lt;/span&gt;
&amp;nbsp;
    ...
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;

В данном примере задача может потерять управление только в двух местах: при вызове системного сервиса &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Yield(). Эта определенность позволяет минимизировать объем сохраняемых при переключении задачи данных. Объем этих данных зависит не только от типа микроконтроллера (программный счетчик, указатель стека и пр.), но и от особенностей конкретного компилятора. Например WinAVR предполагает, что регистры r2..r17 сохраняются самой функцией (если используются в ней), а регистры r18..r27, r30, r31 - применяются только для сиюминутных операций (т.е. подразумевается, что при возврате из вызываемой функции значения этих регистров будет утеряно). Это как минимум позволяет при переключении контекста не сохранять эти регистры. Кроме того, WinAVR предоставляет возможность определять некоторые функции с атрибутом __returns_twice__, который избавляет нас от необходимости сохранять и r2..r17. Т.е. весь сохраняемый контекст будет состоять из: программного счетчика и указателя фрейма (r28:r29). 
&lt;/p&gt;

&lt;p&gt;
Остается проблема с локальными переменными. Есть два пути: 
&lt;/p&gt;
&lt;ol&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; применить такой же подход, какой применяется в вытесняющих ОС (т.е. выделять каждой задаче свой стек);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; условиться, что время жизни локальных переменных ограничено не телом функции, а интервалом между двумя переключениями задач (в нашем примере выше - между двумя &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Yield()).&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

Первый вариант требует наличия сравнительно большого объема оперативной памяти. Т.е. такого объема, который уже позволит использовать вытесняющую ОС, которая гораздо предпочтительнее кооперативной при прочих равных условиях. А вот второй вариант выглядит более приемлемым. Т.е. мы условились, что переменные &lt;strong&gt;x&lt;/strong&gt; и &lt;strong&gt;y&lt;/strong&gt; будут терять свои значения после каждого вызова &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Yield() (т.е. после каждой передачи управления планировщику), и если нам понадобится текущая позиция экрана после вызова &lt;acronym title=&quot;Operating System&quot;&gt;OS&lt;/acronym&gt;_Yield(), то мы уже не сможем воспользоваться прежними значениями x и y, т.к. они уже утеряны, и нам нужно будет снова воспользоваться функциями lcd_getx() и lcd_gety(). Почему значения x и y теряются? На этот вопрос мы уже отвечали: потому что в малоресурсных контроллерах приходится использовать одну область памяти для локальных переменных всех задач, и после переключения задачи значения локальных переменных текущей функции просто затрутся значениями локальных переменных параллельно выполняющейся задачи.
&lt;/p&gt;

&lt;p&gt;
Как быть, если все-таки нужно сохранить значения переменных и после переключения контекста? Очень просто: объявлять из с квалификатором &lt;strong&gt;static&lt;/strong&gt;:

&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; Task &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw4&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; x, y;         &lt;span class=&quot;co1&quot;&gt;// Объявляем переменные вне стека&lt;/span&gt;
&amp;nbsp;
    lcd_init&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;               &lt;span class=&quot;co1&quot;&gt;// инициализация модуля ЖКИ&lt;/span&gt;
&amp;nbsp;
    OS_Yield&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;               &lt;span class=&quot;co1&quot;&gt;// передача управления другим задачам (фактически - планировщику)&lt;/span&gt;
&amp;nbsp;
    x &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; lcd_getx&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;           &lt;span class=&quot;co1&quot;&gt;// вывод &amp;quot;Hello world&amp;quot; в строке под курсором&lt;/span&gt;
    y &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; lcd_gety&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    lcd_outtext&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;x, y &lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;, &lt;span class=&quot;st0&quot;&gt;&amp;quot;Hello world&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    OS_Yield&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;               &lt;span class=&quot;co1&quot;&gt;// передача управления&lt;/span&gt;
&amp;nbsp;
    lcd_outtext&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;x, y &lt;span class=&quot;sy2&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;, &lt;span class=&quot;st0&quot;&gt;&amp;quot;All animals are equal&amp;quot;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
    ...
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
В этом примере мы не боимся потерять x и y, т.к. они имеют статические адреса и стек других задач их не сможет перетереть.
&lt;/p&gt;

&lt;p&gt;
А теперь мы подошли к самой проблеме.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Кооперативная ОС и локальные переменные&quot; [5042-13556] --&gt;
&lt;h3&gt;&lt;a name=&quot;проблема&quot; id=&quot;проблема&quot;&gt;Проблема&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Компилятор WinAVR в области локальных переменных, помимо переменных, объявленных пользователем, размещает еще и свои временные переменные (которые могут использоваться либо для хранения промежуточных результатов расчетов, либо для сокращения кода). Т.е. те переменные, к которым программист не имеет прямого доступа и которые он не может объявить как static. Это для кооперативных планировщиков плохо тем, что компилятор не знает, какие из подпрограмм, вызываемых из функции-задачи, передают управление планировщику. Вернее, есть способ ему об это сказать, объявив такие функции с атрибутом __returns_twice__, но конкретная реализация WinAVR при определенных условиях может это проигнорировать. В результате получается, что значения этих временных переменных изменяется после передачи управления планировщику, а компилятор об этом не знает и продолжает их использовать так, как будто они остались неизменными. В следующем параграфе я привел пример программы, синтезирующей эту ошибку.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Проблема&quot; [13557-15374] --&gt;
&lt;h2&gt;&lt;a name=&quot;тестовая_программа&quot; id=&quot;тестовая_программа&quot;&gt;Тестовая программа&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Тестовая программа&quot; [15375-15423] --&gt;
&lt;h3&gt;&lt;a name=&quot;описание&quot; id=&quot;описание&quot;&gt;Описание&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Для описания ошибки я написал небольшую программу. Я умышленно не стал подключать OSA и сделал все исключительно средствами самого компилятора, а именно - воспользовался парой функций setjmp/longjmp. Суть программы проста: есть две функции task1 и task2, которые по очереди передают друг другу управление. Одна функция вызывает подпрограмму, копирующую в строковую переменную Hello слово &amp;quot;HELLO&amp;quot;, а вторая вызывает подпрограмму, копирующую в строковую переменную World слово &amp;quot;WORLD&amp;quot;.
&lt;/p&gt;

&lt;p&gt;
Как работают функции setjmp() и longjmp, надеюсь, объяснять не нужно. В двух словах для тех, кто с ними не знаком: 
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; функция &lt;strong&gt;setjmp()&lt;/strong&gt; выполняет сохранение контекста (PC, SP, SREG, r2..r17);&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; функция &lt;strong&gt;longjmp()&lt;/strong&gt; выполняет безусловный переход с восстановлением ранее сохраненного контекста.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Описание&quot; [15424-16781] --&gt;
&lt;h3&gt;&lt;a name=&quot;текст_программы&quot; id=&quot;текст_программы&quot;&gt;Текст программы&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

(Можно &lt;span class=&quot;important&quot;&gt;&lt;a href=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/winavr_bug.rar&quot; class=&quot;media mediafile mf_rar&quot; title=&quot;osa:articles:winavr_bug.rar&quot;&gt;скачать&lt;/a&gt;&lt;/span&gt; текст программы с файлами проекта для AVR Studio 4.18)
&lt;/p&gt;
&lt;pre class=&quot;cpp code cpp&quot; style=&quot;font-family:monospace;&quot;&gt;&lt;span class=&quot;co2&quot;&gt;#include &amp;lt;avr/io.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#include &amp;lt;avr/interrupt.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;co2&quot;&gt;#include &amp;lt;setjmp.h&amp;gt;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Переменные&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; Hello&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;  &lt;span class=&quot;co1&quot;&gt;// Буфер для хранения слова &amp;quot;HELLO&amp;quot;&lt;/span&gt;
&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; World&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;  &lt;span class=&quot;co1&quot;&gt;// Буфер для хранения слова &amp;quot;WORLD&amp;quot;&lt;/span&gt;
&amp;nbsp;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;jmp_buf&lt;/span&gt;     j_task1, j_task2;   &lt;span class=&quot;co1&quot;&gt;// Контекст задач&lt;/span&gt;
&amp;nbsp;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Прототипы функций&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; task1          &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; task2          &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; strcpy_hello   &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt; data&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; __attribute__&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;noinline&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; strcpy_world   &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt; data&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; __attribute__&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;noinline&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//  Функции&lt;/span&gt;
&lt;span class=&quot;co1&quot;&gt;//******************************************************************************&lt;/span&gt;
&amp;nbsp;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; main &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    task1&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                &lt;span class=&quot;co1&quot;&gt;// Инициализируем задачи&lt;/span&gt;
    task2&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    &lt;span class=&quot;kw3&quot;&gt;longjmp&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;j_task1, &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;    &lt;span class=&quot;co1&quot;&gt;// Запускаем поочередное выполнение,&lt;/span&gt;
                            &lt;span class=&quot;co1&quot;&gt;// начиная с задачи Taks1&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; strcpy_hello &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt; str&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;      &lt;span class=&quot;co1&quot;&gt;// Cкопировать слово &amp;quot;HELLO&amp;quot; в str&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    str&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'H'&lt;/span&gt;;
    str&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'E'&lt;/span&gt;;
    str&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'L'&lt;/span&gt;;
    str&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'L'&lt;/span&gt;;
    str&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'O'&lt;/span&gt;;
    str&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; strcpy_world &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;sy2&quot;&gt;*&lt;/span&gt; str&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;      &lt;span class=&quot;co1&quot;&gt;// Cкопировать слово &amp;quot;WORLD&amp;quot; в str&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    str&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'W'&lt;/span&gt;;
    str&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'O'&lt;/span&gt;;
    str&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'R'&lt;/span&gt;;
    str&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'L'&lt;/span&gt;;
    str&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;st0&quot;&gt;'D'&lt;/span&gt;;
    str&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; task1 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;                                   &lt;span class=&quot;co1&quot;&gt;// ЗАДАЧА 1&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;kw3&quot;&gt;setjmp&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;j_task1&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;return&lt;/span&gt;;                   &lt;span class=&quot;co1&quot;&gt;// Инициализируем контекст&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        Hello&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; Hello&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;                       &lt;span class=&quot;co1&quot;&gt;// Обращение к двум элементам массива&lt;/span&gt;
                                                    &lt;span class=&quot;co1&quot;&gt;// на чтение и запись&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;kw3&quot;&gt;setjmp&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;j_task1&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw3&quot;&gt;longjmp&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;j_task2, &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;  &lt;span class=&quot;co1&quot;&gt;// Переключение контекста&lt;/span&gt;
        strcpy_hello&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;Hello&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                        &lt;span class=&quot;co1&quot;&gt;// Копируем константу &amp;quot;HELLO&amp;quot; в массив&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;co1&quot;&gt;//------------------------------------------------------------------------------&lt;/span&gt;
&amp;nbsp;
&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt; task2 &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;kw4&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;                                   &lt;span class=&quot;co1&quot;&gt;//  ЗАДАЧА 2&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
    &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;kw3&quot;&gt;setjmp&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;j_task2&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw1&quot;&gt;return&lt;/span&gt;;                   &lt;span class=&quot;co1&quot;&gt;// Инициализируем контекст&lt;/span&gt;
&amp;nbsp;
    &lt;span class=&quot;kw1&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;;;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#123;&lt;/span&gt;
        World&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt; &lt;span class=&quot;sy3&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;sy1&quot;&gt;=&lt;/span&gt; World&lt;span class=&quot;br0&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span class=&quot;nu19&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#93;&lt;/span&gt;;                       &lt;span class=&quot;co1&quot;&gt;// Обращение к двум элементам массива&lt;/span&gt;
                                                    &lt;span class=&quot;co1&quot;&gt;// на чтение и запись&lt;/span&gt;
        &lt;span class=&quot;kw1&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span class=&quot;sy3&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;kw3&quot;&gt;setjmp&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;j_task2&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span class=&quot;kw3&quot;&gt;longjmp&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;j_task1, &lt;span class=&quot;nu0&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;  &lt;span class=&quot;co1&quot;&gt;// Переключение контекста&lt;/span&gt;
        strcpy_world&lt;span class=&quot;br0&quot;&gt;&amp;#40;&lt;/span&gt;World&lt;span class=&quot;br0&quot;&gt;&amp;#41;&lt;/span&gt;;                        &lt;span class=&quot;co1&quot;&gt;// Копируем константу &amp;quot;WORLD&amp;quot; в массив&lt;/span&gt;
    &lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;
&lt;span class=&quot;br0&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
Обращу ваше внимание на некоторые моменты:
&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; функции strcpy_hello() и strpy_world() объявлены с атрибутом __noinline__, чтобы предотвратить оптимизацию прямой подстановкой тела функции в код, т.к. для синтезирования ошибки нам обязательно нужно, чтобы это были именно вызываемые (через rcall) функции;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;level1&quot;&gt;&lt;div class=&quot;li&quot;&gt; в функциях task1() и task2() есть бессмысленные строчки: &amp;quot;Hello[1] ^= Hello[0];&amp;quot; и &amp;quot;World[1] ^= World[0];&amp;quot;. Они не несут никакого функционала, просто для синтезирования ошибки нужно, чтобы перед переключением контекста было произведено обращение к нулевому и первому элементам массива, причем нулевой должен быть на чтение, а первый - на запись.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Текст программы&quot; [16782-21366] --&gt;
&lt;h3&gt;&lt;a name=&quot;запуск_программы&quot; id=&quot;запуск_программы&quot;&gt;Запуск программы&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Откройте проект в AVR Studio и соберите его (F7). Установите две точки останова: 
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.pic24.ru/lib/exe/detail.php/osa/articles/winavr_breakpoints.png?id=osa%3Aarticles%3Awinavr_bug&quot; class=&quot;media&quot; title=&quot;osa:articles:winavr_breakpoints.png&quot;&gt;&lt;img src=&quot;http://www.pic24.ru/lib/exe/fetch.php/osa/articles/winavr_breakpoints.png&quot; class=&quot;media&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Запустите Start-Debugging (Ctlr+Shift+Alt+F5). Далее кнопкой F5 перемещайтесь по программе, а в окне Watch наблюдайте содержимое переменых &lt;strong&gt;Hello&lt;/strong&gt; и &lt;strong&gt;World&lt;/strong&gt;. Вы увидите, что программа постоянно переключается между функциями &lt;strong&gt;task1()&lt;/strong&gt; и &lt;strong&gt;task2()&lt;/strong&gt;. Но самое главное - вы увидите, что присваиваемые слова &amp;quot;HELLO&amp;quot; и &amp;quot;WORLD&amp;quot; копируются только в одну переменную - &lt;strong&gt;World&lt;/strong&gt;, а переменная &lt;strong&gt;Hello&lt;/strong&gt; остается нетронутой. 
&lt;/p&gt;

&lt;p&gt;
Почему?
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Запуск программы&quot; [21367-22256] --&gt;
&lt;h3&gt;&lt;a name=&quot;детальный_разбор&quot; id=&quot;детальный_разбор&quot;&gt;Детальный разбор&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Рассмотрим листинг функции &lt;strong&gt;task1()&lt;/strong&gt; (листинг &lt;strong&gt;task2()&lt;/strong&gt; аналогичен):
&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;000000c2 &amp;#60;task1&amp;#62;:
}

//------------------------------------------------------------------------------

void task1 (void)                                   // ЗАДАЧА 1
{
  c2:   df 93           push    r29
  c4:   cf 93           push    r28
  c6:   00 d0           rcall   .+0
  c8:   cd b7           in      r28, SPL
  ca:   de b7           in      r29, SPH

  cc:   80 e6           ldi     r24, 0x60       ; if (!setjmp(j_task1)) return;
  ce:   90 e0           ldi     r25, 0x00
  d0:   28 d0           rcall   setjmp
  d2:   89 2b           or      r24, r25
  d4:   29 f4           brne    .+10

  d6:   0f 90           pop     r0
  d8:   0f 90           pop     r0
  da:   cf 91           pop     r28
  dc:   df 91           pop     r29
  de:   08 95           ret

                                                ; for (;;)
                                                ; {
  e0:   8d e7           ldi     r24, 0x7D       ;     примечание: 0x7D - адрес переменной Hello
  e2:   90 e0           ldi     r25, 0x00       ;
  e4:   89 83           std     Y+1, r24        ;
  e6:   9a 83           std     Y+2, r25        ;
  e8:   03 c0           rjmp    .+6             ;

  ea:   89 81           ldd     r24, Y+1        ;     strcpy_hello(Hello);
  ec:   9a 81           ldd     r25, Y+2        ;
  ee:   a5 df           rcall   strcpy_hello    ;

  f0:   ed e7           ldi     r30, 0x7D       ;     Hello[1] ^= Hello[0];
  f2:   f0 e0           ldi     r31, 0x00       ;
  f4:   80 81           ld      r24, Z
  f6:   ee e7           ldi     r30, 0x7E       ;
  f8:   f0 e0           ldi     r31, 0x00       ;
  fa:   90 81           ld      r25, Z
  fc:   89 27           eor     r24, r25
  fe:   80 83           st      Z, r24


 100:   80 e6           ldi     r24, 0x60       ; if (!setjmp(j_task1))
 102:   90 e0           ldi     r25, 0x00       ;
 104:   0e d0           rcall   setjmp          ;
 106:   89 2b           or      r24, r25
 108:   81 f7           brne    0xEA

 10a:   83 e8           ldi     r24, 0x83       ; longjmp(j_task2, 1);
 10c:   90 e0           ldi     r25, 0x00       ;
 10e:   61 e0           ldi     r22, 0x01       ;
 110:   70 e0           ldi     r23, 0x00       ;
 112:   28 d0           rcall   longjmp         ;&lt;/pre&gt;
&lt;p&gt;
В адресах 0xC2..0xCA производится выделение 2-байтового фрейма в стеке. Далее, в адресах 0xCC..0xDE производится формирование контекста. Нас же интересует работа программы в адресах 0xE0..0x112, т.е. работа цикла.
&lt;/p&gt;

&lt;p&gt;
Сразу же обратим внимание на код 0xE0..0xE6, в котором адрес строки Hello копируется во &lt;em class=&quot;u&quot;&gt;временную&lt;/em&gt; локальную переменную [Y+1]:[Y+2] (еще раз посмотрим код и убедимся, что сами мы локальных переменных не создавали). Далее (адрес 0xE8) происходит переход на операцию Hello[1]^=Hello[0] (адреса 0xF0..0xFE), затем попадаем на сохранение контекста setjmp() (0x100..0x104) и передачу управления задаче task2() через longjmp() (0x10A..0x112). 
&lt;/p&gt;

&lt;p&gt;
При передаче управления восстанавливается контекст для функции task2, в том числе указатель фрейма r28:r29 (т.е. Y). Т.к. функции идентичны, то и фрейм для них будет одного размера (2 байта) и расположен по &lt;em class=&quot;u&quot;&gt;одним и тем же адресам&lt;/em&gt;. Функция task2() выполняет абсолютно те же действия, что и task1(), а что самое главное - производит копирование адреса переменной World во временную переменную [Y+1]:[Y+2], &lt;em class=&quot;u&quot;&gt;затирая значение&lt;/em&gt;, записанное туда функцией task1(). Дойдя тем же путем, что и task1(), до вызова longjmp(), функция task2() передает управление task1() через сохраненный контекст j_task1. 
&lt;/p&gt;

&lt;p&gt;
Тут и начинаются проблемы. Программный счетчик восстанавливается и указывает на следующую за вызовом setjmp() команду, т.е. на адрес 0x89. После чего выполняется переход на адрес 0xEA (после перехода longjmp() r25 != r24), где и располагается вызов функции копирования строки strcpy_hello(). Обратите внимание, что в качестве параметра ей передается не фактический адрес переменной Hello, а значение, запомненное во временной переменной [Y+1]:[Y+2], т.е. то, которое было перетерто функцией task2(), и теперь по этим адресам хранится адрес не переменной Hello, а переменной World. &lt;strong&gt;Поэтому функция strcpy_hello запишет слово &amp;quot;HELLO&amp;quot; в переменную World, а не в Hello&lt;/strong&gt;.
&lt;/p&gt;

&lt;p&gt;
Вот такая проблема.
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Детальный разбор&quot; [22257-28063] --&gt;
&lt;h2&gt;&lt;a name=&quot;заключение&quot; id=&quot;заключение&quot;&gt;Заключение&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;level2&quot;&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Заключение&quot; [28064-28097] --&gt;
&lt;h3&gt;&lt;a name=&quot;почему_я_это_назвал_ошибкой&quot; id=&quot;почему_я_это_назвал_ошибкой&quot;&gt;Почему я это назвал ошибкой&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

Хоть я и создал пример, синтезирующий описанную ошибку, но мне так и не удалось четко сформулировать условия ее проявления. Попробуйте модифицировать код (например, убрать выражение Hello[1] ^= Hello[0], или поменять в нем индексы, или убрать setjmp/longjmp) и программа заработает, т.е. начнет передавать в функцию strcpy_hello() фактический адрес переменной, а не его копию. Т.е. я не могу точно сказать, что в таком-то случае ошибка будет проявляться, а в таком не будет. Заметить удалось только то, что между выражением, обращающимся к тому же адресу, что и вызов функции strcpy_hello(), должен находиться вызов функции с атрибутом __returns_twice__. 
&lt;/p&gt;

&lt;p&gt;
Учитывая, что атрибут функции __returns_twice__ говорит компилятору, что все значения регистров теряются после возврата из такой функции, предполагается, что он в курсе насчет того, что и локальные переменные могут быть утеряны. Из описания:

&lt;/p&gt;
&lt;pre class=&quot;code&quot;&gt;__returns_twice__:
  The returns_twice attribute tells the  compiler that a function may return
  more than one time. The compiler will ensure that  all registers are  dead
  before calling such a function and will emit a warning about the variables
  that may be clobbered after the second return from the function.  Examples
  of such functions are  setjmp  and  vfork. The longjmp-like counterpart of
  such function, if any, might need to be marked with the noreturn attribute.&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- SECTION &quot;Почему я это назвал ошибкой&quot; [28098-30222] --&gt;
&lt;h3&gt;&lt;a name=&quot;как_решить_проблему&quot; id=&quot;как_решить_проблему&quot;&gt;Как решить проблему&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;level3&quot;&gt;

&lt;p&gt;

&lt;span class=&quot;important&quot;&gt;Для OSA с версии 100531 найден механизм обхода этой проблемы&lt;/span&gt;
&lt;/p&gt;

&lt;p&gt;
Я нашел только одно проявление этой проблемы, могут быть и другие. Как побороть компилятор и заставить его не пользоваться временными переменными при обнаружении вызовов функций __returns_twice__, я пока не придумал. Объявление переменных с квалификатором volatile не спасает (ни глобальных переменных, ни параметров функций). 
&lt;/p&gt;

&lt;p&gt;
Единственное решение, которое пока пришло в голову - это отключение оптимизации. Без оптимизации компилятор, если и задумает воспользоваться временной переменной, то все равно занесет в нее значение столько раз, сколько оно понадобится, а не один раз на все случаи, как с оптимизацией.
&lt;/p&gt;

&lt;p&gt;

Если у кого-то есть какие-то соображения, буду рад любой помощи.
&lt;/p&gt;

&lt;p&gt;
Виктор Тимофеев, март 2010
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;mailto:&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot; class=&quot;mail JSnocheck&quot; title=&quot;&amp;#x6f;&amp;#x73;&amp;#x61;&amp;#x40;&amp;#x70;&amp;#x69;&amp;#x63;&amp;#x32;&amp;#x34;&amp;#x2e;&amp;#x72;&amp;#x75;&quot;&gt;osa@pic24.ru&lt;/a&gt;
&lt;/p&gt;

&lt;/div&gt;
&lt;!-- SECTION &quot;Как решить проблему&quot; [30223-] --&gt;</description>
    </item>
</rdf:RDF>
