<?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>2010-09-07T05:55:46+04: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=1280393468"/>
                <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/pk2_osa_lights?rev=1249632117"/>
                <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_usage?rev=1274795982"/>
                <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=1248727357"/>
                <rdf:li rdf:resource="http://www.pic24.ru/doku.php/osa/articles/vga_terminal?rev=1270020612"/>
                <rdf:li rdf:resource="http://www.pic24.ru/doku.php/osa/articles/volatile_for_chainiks?rev=1277134978"/>
                <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+04: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=1280393468">
        <dc:format>text/html</dc:format>
        <dc:date>2010-07-29T12:51:08+04:00</dc:date>
        <title>Статьи Виктора Тимофеева</title>
        <link>http://www.pic24.ru/doku.php/osa/articles/intro?rev=1280393468</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;/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+04: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/pk2_osa_lights?rev=1249632117">
        <dc:format>text/html</dc:format>
        <dc:date>2009-08-07T12:01:57+04:00</dc:date>
        <title>&quot;Бегущие огни&quot; на ОСРВ</title>
        <link>http://www.pic24.ru/doku.php/osa/articles/pk2_osa_lights?rev=1249632117</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-11337] --&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; [11338-11953] --&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; [11954-13196] --&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; [13197-15477] --&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; [15478-17001] --&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; [17002-22319] --&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; [22320-29507] --&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; [29508-31918] --&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; [31919-34801] --&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; [34802-39774] --&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; [39775-40554] --&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; [40555-44770] --&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; [44771-51774] --&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; [51775-52035] --&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; [52036-53829] --&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; [53830-54293] --&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; [54294-] --&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+04: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;sp