<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="rss.xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>EmbedBits Blog</title>
        <link>https://embedbits.cz/uk/blog</link>
        <description>EmbedBits Blog</description>
        <lastBuildDate>Wed, 07 Oct 2026 20:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>uk</language>
        <item>
            <title><![CDATA[Налагодження HardFault на Cortex-M: від загадки до рядка коду]]></title>
            <link>https://embedbits.cz/uk/blog/debugging-hardfault-cortex-m</link>
            <guid>https://embedbits.cz/uk/blog/debugging-hardfault-cortex-m</guid>
            <pubDate>Wed, 07 Oct 2026 20:00:00 GMT</pubDate>
            <description><![CDATA[Кожен розробник вбудованих систем знає цей момент у вісім регістрів на стеку та в чотири регістри стану. Їх залишається лише прочитати.]]></description>
            <content:encoded><![CDATA[<p>Кожен розробник вбудованих систем знає цей момент: прошивка працювала, а потім перестала. Налагоджувач показує, що програма сидить у нескінченному циклі з назвою на кшталт <code>HardFault_Handler</code> або <code>Default_Handler</code>, а стек викликів — це кілька беззмістовних фреймів. Цей цикл — <strong>типовий</strong> обробник зі startup-коду, і він не каже абсолютно нічого про те, що сталося. У збою є причина, і процесор уже її записав: у вісім регістрів на стеку та в чотири регістри стану. Їх залишається лише прочитати.</p>
<p>Ця стаття показує, як перетворити цей цикл на рядок вихідного коду, на двох реальних прикладах, які я запускав у QEMU (моделі плати з STM32F4) і досліджував за допомогою GDB: виклик <code>NULL</code>-вказівника на функцію та запис за адресою, де нічого немає. Усі виводи справжні.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="що-робить-ядро-коли-стається-збій">Що робить ядро, коли стається збій<a href="https://embedbits.cz/uk/blog/debugging-hardfault-cortex-m#%D1%89%D0%BE-%D1%80%D0%BE%D0%B1%D0%B8%D1%82%D1%8C-%D1%8F%D0%B4%D1%80%D0%BE-%D0%BA%D0%BE%D0%BB%D0%B8-%D1%81%D1%82%D0%B0%D1%94%D1%82%D1%8C%D1%81%D1%8F-%D0%B7%D0%B1%D1%96%D0%B9" class="hash-link" aria-label="Пряме посилання на Що робить ядро, коли стається збій" title="Пряме посилання на Що робить ядро, коли стається збій" translate="no">​</a></h2>
<p>На Cortex-M процесор не падає мовчки. Коли він не може продовжувати (недійсна інструкція, помилка шини, недозволений доступ), він переходить до <strong>винятку</strong> (exception) і робить дві речі, важливі для налагодження:</p>
<ol>
<li class=""><strong>Заштовхує вісім регістрів на стек</strong>: <code>R0</code>, <code>R1</code>, <code>R2</code>, <code>R3</code>, <code>R12</code>, <code>LR</code>, <code>PC</code> і <code>xPSR</code>. Збережений <code>PC</code> — це інструкція, що спричинила збій, а збережений <code>LR</code> вказує, хто викликав функцію, у якій стався збій.</li>
<li class="">Встановлює <strong>біти в регістрах стану збоїв</strong>, які показують, який це був збій: <code>CFSR</code> (configurable fault status, що складається з трьох частин для збоїв пам'яті, шини та використання), <code>HFSR</code> (hard fault status) і регістри адрес <code>MMFAR</code> та <code>BFAR</code> з адресою, до якої було звернення.</li>
</ol>
<p>Збої пам'яті, шини та використання мають власні обробники, але після скидання вони <strong>вимкнені</strong>, тому кожен збій <em>ескалується</em> до HardFault. Ось чому один обробник бачить усе, а біт <code>FORCED</code> в <code>HFSR</code> повідомляє, що це сталося.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="обробник-який-читає-регістри">Обробник, який читає регістри<a href="https://embedbits.cz/uk/blog/debugging-hardfault-cortex-m#%D0%BE%D0%B1%D1%80%D0%BE%D0%B1%D0%BD%D0%B8%D0%BA-%D1%8F%D0%BA%D0%B8%D0%B9-%D1%87%D0%B8%D1%82%D0%B0%D1%94-%D1%80%D0%B5%D0%B3%D1%96%D1%81%D1%82%D1%80%D0%B8" class="hash-link" aria-label="Пряме посилання на Обробник, який читає регістри" title="Пряме посилання на Обробник, який читає регістри" translate="no">​</a></h2>
<p>Єдина складність — дістатися до стека, де лежать регістри. У ядра є два вказівники стека (<code>MSP</code> для винятків і основного коду, <code>PSP</code> для задач операційної системи), і біт 2 значення в <code>LR</code> на вході у виняток показує, який із них використовувався. Це потрібно зробити <em>до того</em>, як компілятор використає стек для власних потреб, тому обробник — це коротка функція на асемблері (<code>naked</code>: без пролога), яка передає вказівник у функцію на C:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">fault.c (the report)</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ---- the fault report ---- */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">SCB_CFSR</span><span class="token macro property">  </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">*</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression keyword" style="font-style:italic">volatile</span><span class="token macro property expression"> </span><span class="token macro property expression class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token macro property expression"> </span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">*</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">0xE000ED28u</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token macro property expression">   </span><span class="token macro property comment" style="color:rgb(105, 112, 152);font-style:italic">/* configurable fault status */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">SCB_HFSR</span><span class="token macro property">  </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">*</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression keyword" style="font-style:italic">volatile</span><span class="token macro property expression"> </span><span class="token macro property expression class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token macro property expression"> </span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">*</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">0xE000ED2Cu</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token macro property expression">   </span><span class="token macro property comment" style="color:rgb(105, 112, 152);font-style:italic">/* hard fault status */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">SCB_MMFAR</span><span class="token macro property"> </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">*</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression keyword" style="font-style:italic">volatile</span><span class="token macro property expression"> </span><span class="token macro property expression class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token macro property expression"> </span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">*</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">0xE000ED34u</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">SCB_BFAR</span><span class="token macro property">  </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">*</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression keyword" style="font-style:italic">volatile</span><span class="token macro property expression"> </span><span class="token macro property expression class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token macro property expression"> </span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">*</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">0xE000ED38u</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* The registers that the core pushed on the stack when the fault happened. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">struct</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> r0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> r1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> r2</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> r3</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> r12</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> lr</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">     </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* where the faulting code was called from */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> pc</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">     </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* the instruction that caused the fault   */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> xpsr</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token class-name" style="color:rgb(255, 203, 107)">stackFrame_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">volatile</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">struct</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">stackFrame_t</span><span class="token plain"> frame</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain">     cfsr</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> hfsr</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> mmfar</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> bfar</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   faultInfo</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">HardFault_Report</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">stackFrame_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">frame</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">__attribute__</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">used</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">HardFault_Report</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">stackFrame_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">frame</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    faultInfo</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">frame </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">frame</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    faultInfo</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">cfsr  </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> SCB_CFSR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    faultInfo</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">hfsr  </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> SCB_HFSR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    faultInfo</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">mmfar </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> SCB_MMFAR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    faultInfo</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">bfar  </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> SCB_BFAR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Print</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"*** HardFault ***\n"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">PrintHex</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"PC   "</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> frame</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">pc</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">PrintHex</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"LR   "</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> frame</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">lr</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">PrintHex</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"xPSR "</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> frame</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">xpsr</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">PrintHex</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"CFSR "</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> faultInfo</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">cfsr</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">PrintHex</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"HFSR "</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> faultInfo</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">hfsr</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">for</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">          </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* stay here, so that a debugger can look at the state */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* The core uses the main or the process stack, depending on bit 2 of EXC_RETURN (in LR). */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">__attribute__</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">naked</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">HardFault_Handler</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    __asm__ </span><span class="token keyword" style="font-style:italic">volatile</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token string" style="color:rgb(195, 232, 141)">"tst   lr, #4        \n"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token string" style="color:rgb(195, 232, 141)">"ite   eq            \n"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token string" style="color:rgb(195, 232, 141)">"mrseq r0, msp       \n"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token string" style="color:rgb(195, 232, 141)">"mrsne r0, psp       \n"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token string" style="color:rgb(195, 232, 141)">"b     HardFault_Report \n"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Погляньте на три частини. Структура <code>stackFrame_t</code> — це розташування восьми регістрів у порядку, в якому їх заштовхує ядро. Функція <code>HardFault_Report()</code> копіює їх (разом з регістрами стану) у глобальну змінну <code>faultInfo</code>, друкує короткий звіт, а потім <strong>навмисно залишається в циклі</strong>, щоб налагоджувач міг підключитися й подивитися на змінну. Асемблерна заглушка вибирає правильний стек і переходить до функції. У продукті звіт записувався б у пам'ять, що переживає скидання, а прошивка самостійно перезавантажувалася б, але принцип той самий.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="приклад-1-виклик-null-вказівника">Приклад 1: виклик <code>NULL</code>-вказівника<a href="https://embedbits.cz/uk/blog/debugging-hardfault-cortex-m#%D0%BF%D1%80%D0%B8%D0%BA%D0%BB%D0%B0%D0%B4-1-%D0%B2%D0%B8%D0%BA%D0%BB%D0%B8%D0%BA-null-%D0%B2%D0%BA%D0%B0%D0%B7%D1%96%D0%B2%D0%BD%D0%B8%D0%BA%D0%B0" class="hash-link" aria-label="Пряме посилання на приклад-1-виклик-null-вказівника" title="Пряме посилання на приклад-1-виклик-null-вказівника" translate="no">​</a></h2>
<p>Ось застосунок із помилкою. Callback реєструється пізніше, а код, який його викликає, виконується раніше:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">fault.c (the application)</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ---- the application with a bug ---- */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token class-name" style="color:rgb(255, 203, 107)">callback_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">callback_t</span><span class="token plain"> onButtonPressed</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">      </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* will be registered later: it is NULL at the start */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_Pressed</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">onButtonPressed</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">                  </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* the bug: the callback is called before it is set */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">int</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">main</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Print</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"start\n"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Button_Pressed</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Print</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"never printed\n"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Програма друкує <code>start</code>, а потім звіт:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">start</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">*** HardFault ***</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">PC    0x00000000</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">LR    0x08000115</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">xPSR  0x60000000</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">CFSR  0x00020000</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">HFSR  0x40000000</span><br></div></code></pre></div></div>
<p>Тепер його розбір, значення за значенням:</p>
<ul>
<li class=""><strong><code>PC = 0x00000000</code></strong>: процесор спробував виконати інструкцію за нульовою адресою. Майже завжди це означає перехід через <code>NULL</code>-вказівник на функцію або повернення за пошкодженою адресою.</li>
<li class=""><strong><code>CFSR = 0x00020000</code></strong>: біт 17 — це <code>INVSTATE</code>, «недійсний стан». Процесор спробував виконувати код у стані ARM, якого в Cortex-M немає. Причина — в <strong><code>xPSR</code></strong>: його біт 24 — це біт <code>T</code> (Thumb), і тут він нульовий (<code>0x60000000</code>). Адреса Thumb-функції має встановлений наймолодший біт, а <code>NULL</code> його не має, тож перехід через вказівник скинув біт <code>T</code>. Нульовий біт <code>T</code> — типовий відбиток переходу на <code>NULL</code>.</li>
<li class=""><strong><code>HFSR = 0x40000000</code></strong>: біт 30 — це <code>FORCED</code>, збій було ескальовано до HardFault зі збою використання (usage fault), який не ввімкнено.</li>
<li class=""><strong><code>LR = 0x08000115</code></strong>: адреса повернення з встановленим наймолодшим бітом (Thumb). Це адреса <strong>викликача</strong>, <code>0x08000114</code>, і саме там помилка.</li>
</ul>
<p>З налагоджувачем останній крок займає лічені секунди. QEMU чекає на GDB (<code>-S -gdb tcp::1234</code>), і сесія виглядає так:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">(gdb) break fault.c:64</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">(gdb) continue</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">Breakpoint 1, HardFault_Report (frame=0x2001ffc0) at fault.c:64</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">(gdb) print/x faultInfo.frame</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">$1 = {r0 = 0xdeadbeef, r1 = 0x80001f4, r2 = 0x8000220, r3 = 0x0, r12 = 0x0,</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      lr = 0x8000115, pc = 0x0, xpsr = 0x60000000}</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">(gdb) print/x faultInfo.cfsr</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">$2 = 0x20000</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">(gdb) info symbol faultInfo.frame.lr</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">Button_Pressed + 7 in section .text</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">(gdb) list *(faultInfo.frame.lr &amp; ~1u)</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">0x8000114 is in Button_Pressed (fault.c:87).</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">82	static callback_t onButtonPressed;      /* will be registered later: it is NULL at the start */</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">84	void Button_Pressed(void)</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">85	{</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">86	    onButtonPressed();                  /* the bug: the callback is called before it is set */</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">87	}</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">(gdb) x/2i (faultInfo.frame.lr &amp; ~1u) - 4</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   0x8000110 &lt;Button_Pressed+2&gt;:	movs	r3, #0</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   0x8000112 &lt;Button_Pressed+4&gt;:	blx	r3</span><br></div></code></pre></div></div>
<p><code>info symbol</code> дає функцію, <code>list *</code> дає рядок (той, що <em>після</em> виклику, бо це адреса повернення), а дизасемблер показує пару інструкцій, яка і є самою помилкою: завантаження нуля в <code>r3</code> та <code>blx r3</code>, виклик через регістр, що містить <code>NULL</code>. Зверніть увагу на <code>r3 = 0x0</code> у збережених регістрах. Без налагоджувача ту саму відповідь дає одна команда toolchain, якій потрібні лише адреса зі звіту та файл з налагоджувальною інформацією (<code>-g</code>):</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">$ arm-none-eabi-addr2line -e fault.elf -f 0x08000114</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">Button_Pressed</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">fault.c:87</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="приклад-2-запис-за-адресою-де-нічого-немає">Приклад 2: запис за адресою, де нічого немає<a href="https://embedbits.cz/uk/blog/debugging-hardfault-cortex-m#%D0%BF%D1%80%D0%B8%D0%BA%D0%BB%D0%B0%D0%B4-2-%D0%B7%D0%B0%D0%BF%D0%B8%D1%81-%D0%B7%D0%B0-%D0%B0%D0%B4%D1%80%D0%B5%D1%81%D0%BE%D1%8E-%D0%B4%D0%B5-%D0%BD%D1%96%D1%87%D0%BE%D0%B3%D0%BE-%D0%BD%D0%B5%D0%BC%D0%B0%D1%94" class="hash-link" aria-label="Пряме посилання на Приклад 2: запис за адресою, де нічого немає" title="Пряме посилання на Приклад 2: запис за адресою, де нічого немає" translate="no">​</a></h2>
<p>Я змінив один рядок застосунку: замість callback функція пише за адресою <code>0xA0000000</code>, де в моделі чипа нічого не відображено. Звіт:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">*** HardFault ***</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">PC    0x08000124</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">LR    0x08000199</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">xPSR  0x21000000</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">CFSR  0x00008200</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">HFSR  0x40000000</span><br></div></code></pre></div></div>
<p>Цього разу <code>PC</code> — це <strong>дійсна адреса у flash</strong>, біт <code>T</code> встановлено (<code>0x21000000</code>), а <code>CFSR</code> інший: <code>0x8200</code> — це два біти, 9 (<code>PRECISERR</code>, точна помилка шини даних) і 15 (<code>BFARVALID</code>, регістр <code>BFAR</code> містить адресу). У GDB:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">(gdb) print/x faultInfo.cfsr</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">$1 = 0x8200</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">(gdb) print/x faultInfo.bfar</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">$2 = 0xa0000000</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">(gdb) info symbol faultInfo.frame.pc</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">main + 12 in section .text</span><br></div></code></pre></div></div>
<p><code>BFAR</code> — це адреса, за якою програма намагалася писати, <code>PC</code> — інструкція, що намагалася це зробити. Разом вони кажуть: «ця інструкція писала за цією адресою», і це повний опис проблеми. Якщо адреса мала (<code>0x00000008</code>), це <code>NULL</code>-вказівник на структуру, і ви звертаєтеся до її поля. Якщо вона схожа на дані (<code>0x20000000</code> і трохи більше), то це стек або буфер, який ви переповнили.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="біти-cfsr-які-ви-зустрінете">Біти CFSR, які ви зустрінете<a href="https://embedbits.cz/uk/blog/debugging-hardfault-cortex-m#%D0%B1%D1%96%D1%82%D0%B8-cfsr-%D1%8F%D0%BA%D1%96-%D0%B2%D0%B8-%D0%B7%D1%83%D1%81%D1%82%D1%80%D1%96%D0%BD%D0%B5%D1%82%D0%B5" class="hash-link" aria-label="Пряме посилання на Біти CFSR, які ви зустрінете" title="Пряме посилання на Біти CFSR, які ви зустрінете" translate="no">​</a></h2>
<p>Регістр є комбінацією трьох регістрів (кожен має власний байт або два байти). Ось ті, які ви бачитимете найчастіше (два, які я спостерігав у прикладах, виділено):</p>
















































































<table><thead><tr><th>Біт</th><th>Назва</th><th>Значення</th></tr></thead><tbody><tr><td>0</td><td><code>IACCVIOL</code></td><td>вибірка інструкції з недозволеної області (MPU)</td></tr><tr><td>1</td><td><code>DACCVIOL</code></td><td>доступ до даних у недозволеній області (MPU)</td></tr><tr><td>7</td><td><code>MMARVALID</code></td><td>регістр <code>MMFAR</code> містить адресу</td></tr><tr><td>8</td><td><code>IBUSERR</code></td><td>помилка шини під час вибірки інструкції</td></tr><tr><td><strong>9</strong></td><td><strong><code>PRECISERR</code></strong></td><td><strong>точна помилка шини під час доступу до даних: <code>PC</code> — це інструкція, що її спричинила</strong></td></tr><tr><td>10</td><td><code>IMPRECISERR</code></td><td>неточна помилка шини: інструкція вже виконалася, <code>PC</code> ненадійний</td></tr><tr><td>11, 12</td><td><code>UNSTKERR</code>, <code>STKERR</code></td><td>помилка шини під час витягування або заштовхування регістрів (часто пошкоджений стек)</td></tr><tr><td><strong>15</strong></td><td><strong><code>BFARVALID</code></strong></td><td><strong>регістр <code>BFAR</code> містить адресу</strong></td></tr><tr><td>16</td><td><code>UNDEFINSTR</code></td><td>невизначена інструкція (виконання даних, пошкоджений код)</td></tr><tr><td><strong>17</strong></td><td><strong><code>INVSTATE</code></strong></td><td><strong>спроба виконання в стані ARM: перехід за адресою з очищеним бітом 0</strong></td></tr><tr><td>18</td><td><code>INVPC</code></td><td>недійсний <code>EXC_RETURN</code> (пошкоджений стек винятку)</td></tr><tr><td>19</td><td><code>NOCP</code></td><td>доступ до співпроцесора, який не ввімкнено (зазвичай FPU, який не було увімкнено)</td></tr><tr><td>24</td><td><code>UNALIGNED</code></td><td>невирівняний доступ, коли trap увімкнено</td></tr><tr><td>25</td><td><code>DIVBYZERO</code></td><td>ділення на нуль, коли trap увімкнено</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="що-зазвичай-стоїть-за-hardfault">Що зазвичай стоїть за HardFault<a href="https://embedbits.cz/uk/blog/debugging-hardfault-cortex-m#%D1%89%D0%BE-%D0%B7%D0%B0%D0%B7%D0%B2%D0%B8%D1%87%D0%B0%D0%B9-%D1%81%D1%82%D0%BE%D1%97%D1%82%D1%8C-%D0%B7%D0%B0-hardfault" class="hash-link" aria-label="Пряме посилання на Що зазвичай стоїть за HardFault" title="Пряме посилання на Що зазвичай стоїть за HardFault" translate="no">​</a></h2>

































<table><thead><tr><th>Що ви бачите</th><th>Чим це зазвичай є</th></tr></thead><tbody><tr><td><code>PC = 0</code>, <code>INVSTATE</code></td><td>виклик через <code>NULL</code>-вказівник на функцію або повернення за пошкодженою адресою</td></tr><tr><td><code>PRECISERR</code> і малий <code>BFAR</code></td><td><code>NULL</code>-вказівник на структуру</td></tr><tr><td><code>PRECISERR</code> і <code>BFAR</code> в діапазоні адрес периферії</td><td>доступ до недоступної периферії, наприклад такої, тактування якої не ввімкнено (в одних сімействах такий доступ — це помилка шини, в інших він мовчки ігнорується)</td></tr><tr><td><code>STKERR</code> / <code>UNSTKERR</code> або <code>PC</code>, що не має сенсу</td><td>переповнення стека або буфера, яке пошкодило адресу повернення</td></tr><tr><td><code>NOCP</code> на першій інструкції з плаваючою комою</td><td>FPU не було увімкнено у startup-коді</td></tr><tr><td><code>IMPRECISERR</code></td><td>запис, що пройшов через буфер запису: звіт приходить пізніше за інструкцію. Для налагодження буферизацію можна вимкнути (біт <code>DISABLEDEFWRITEBUF</code> в <code>ACTLR</code>), і тоді збій стає точним</td></tr></tbody></table>
<p>Я продемонстрував перший рядок таблиці та принцип другого (помилка шини з адресою в <code>BFAR</code>). Решта рядків — це підсумок типових причин, вони випливають зі значення бітів у reference manual ядра.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="практика">Практика<a href="https://embedbits.cz/uk/blog/debugging-hardfault-cortex-m#%D0%BF%D1%80%D0%B0%D0%BA%D1%82%D0%B8%D0%BA%D0%B0" class="hash-link" aria-label="Пряме посилання на Практика" title="Пряме посилання на Практика" translate="no">​</a></h2>
<ol>
<li class=""><strong>Ніколи не залишайте типовий обробник порожнім циклом.</strong> Обробник вище займає близько тридцяти рядків і перетворює збій на звіт із місцем у коді. Додайте його до startup-коду проєкту один раз.</li>
<li class=""><strong>Зберігайте налагоджувальну інформацію</strong> у файлі, який архівуєте разом з релізом (<code>-g</code> і <code>.elf</code>), щоб адресу зі звіту з поля можна було перетворити на рядок, навіть якщо прошивка постачається зі stripped-символами. Це ще одна причина для <a class="" href="https://embedbits.cz/uk/blog/reproducible-builds-embedded">відтворюваної збірки</a>: адреса має сенс лише для точного бінарного файлу, який її створив.</li>
<li class=""><strong>Вмикайте тонші збої</strong> (біти в <code>SHCSR</code>), коли хочете окремі обробники <code>UsageFault</code>, <code>BusFault</code> і <code>MemManage</code>. Звіт буде багатшим, бо регістри стану не спільні.</li>
<li class=""><strong>Зберігайте звіт у пам'яті, що переживає скидання</strong> (секція, яку startup-код не очищує, див. статтю про <a class="" href="https://embedbits.cz/uk/blog/before-main-startup-linker">те, що відбувається перед <code>main()</code></a>), перезавантажуйтеся й надсилайте його після перезапуску. Звіт з пристрою в полі вартий більше за сотню здогадок.</li>
<li class=""><strong>Запобігайте тому, чому можете.</strong> <code>NULL</code>-виклик у прикладі — це дефект, який виявили б <a class="" href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice">правила MISRA</a> та unit-тест модуля (вказівник має бути встановлений до першого виклику), а переповнення стека — це те, для чого потрібно знати використання стека (правило проти рекурсії).</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="та-сама-сесія-на-платі">Та сама сесія на платі<a href="https://embedbits.cz/uk/blog/debugging-hardfault-cortex-m#%D1%82%D0%B0-%D1%81%D0%B0%D0%BC%D0%B0-%D1%81%D0%B5%D1%81%D1%96%D1%8F-%D0%BD%D0%B0-%D0%BF%D0%BB%D0%B0%D1%82%D1%96" class="hash-link" aria-label="Пряме посилання на Та сама сесія на платі" title="Пряме посилання на Та сама сесія на платі" translate="no">​</a></h2>
<p>Я користувався QEMU, бо йому не потрібна плата, а його GDB-сервер (<code>-S -gdb tcp::1234</code>) поводиться так само, як сервер налагоджувального зонда: команди в прикладах — це команди, які ви вводите із зондом. Платформа має артефакт для набору інструментів <strong>probe-rs</strong> (набір інструментів налагодження для ARM-цілей через налагоджувальний зонд, із DAP-сервером), який є способом підключити GDB або IDE до справжньої плати. Я не запускав його для цієї статті, бо в моєму середовищі немає зонда.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="підсумок">Підсумок<a href="https://embedbits.cz/uk/blog/debugging-hardfault-cortex-m#%D0%BF%D1%96%D0%B4%D1%81%D1%83%D0%BC%D0%BE%D0%BA" class="hash-link" aria-label="Пряме посилання на Підсумок" title="Пряме посилання на Підсумок" translate="no">​</a></h2>
<ol>
<li class="">HardFault — не загадка: ядро заштовхнуло регістри та встановило біти стану.</li>
<li class="">Обробнику потрібно знайти правильний стек (<code>EXC_RETURN</code>) і прочитати фрейм, <code>CFSR</code>, <code>HFSR</code> та <code>BFAR</code>.</li>
<li class=""><code>PC</code> і <code>LR</code> дають місце, <code>CFSR</code> — вид збою, <code>BFAR</code> — адресу.</li>
<li class=""><code>PC = 0</code> з <code>INVSTATE</code> — це перехід на <code>NULL</code>, і це показує біт <code>T</code> в <code>xPSR</code>.</li>
<li class="">Зберігайте <code>.elf</code>, і адреса зі звіту з поля стане рядком коду.</li>
</ol>]]></content:encoded>
            <category>Вбудовані системи</category>
            <category>C</category>
            <category>STM32</category>
        </item>
        <item>
            <title><![CDATA[Запуск справжньої прошивки без апаратної частини: симулятор у CI]]></title>
            <link>https://embedbits.cz/uk/blog/firmware-in-simulator-ci</link>
            <guid>https://embedbits.cz/uk/blog/firmware-in-simulator-ci</guid>
            <pubDate>Wed, 07 Oct 2026 18:00:00 GMT</pubDate>
            <description><![CDATA[У статті про unit-тестування з Unity та CMock я казав, що тести на ПК не знаходять «відмінностей між ПК і MCU». Ця стаття — про ці відмінності та про інструмент, який їх знаходить: симулятор, що запускає справжній бінарний файл. Не логіку, зібрану для ПК, а саме той .elf, який створив ARM-компілятор, зі справжнім startup-кодом і справжнім linker script, на моделі процесора та його периферії.]]></description>
            <content:encoded><![CDATA[<p>У статті про <a class="" href="https://embedbits.cz/uk/blog/unit-testing-unity-cmock">unit-тестування з Unity та CMock</a> я казав, що тести на ПК не знаходять «відмінностей між ПК і MCU». Ця стаття — про ці відмінності та про інструмент, який їх знаходить: <strong>симулятор, що запускає справжній бінарний файл</strong>. Не логіку, зібрану для ПК, а саме той <code>.elf</code>, який створив ARM-компілятор, зі справжнім startup-кодом і справжнім linker script, на моделі процесора та його периферії.</p>
<p>Я покажу два експерименти, які провів: той самий вихідний код тесту, що проходить на ПК і падає на Cortex-M4, та прошивку, яка пише в регістри USART за справжніми адресами STM32F4, а її текст з'являється в терміналі. Наприкінці — чесний перелік того, чого симулятор вам не скаже.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="де-це-вписується">Де це вписується<a href="https://embedbits.cz/uk/blog/firmware-in-simulator-ci#%D0%B4%D0%B5-%D1%86%D0%B5-%D0%B2%D0%BF%D0%B8%D1%81%D1%83%D1%94%D1%82%D1%8C%D1%81%D1%8F" class="hash-link" aria-label="Пряме посилання на Де це вписується" title="Пряме посилання на Де це вписується" translate="no">​</a></h2>





























<table><thead><tr><th>Рівень</th><th>Що виконується</th><th>Що знаходить</th><th>Швидкість</th></tr></thead><tbody><tr><td><strong>Unit-тест на ПК</strong></td><td>логіка, зібрана для ПК, нижні шари замоковані</td><td>помилки в логіці</td><td>мілісекунди</td></tr><tr><td><strong>Симулятор</strong></td><td>справжній бінарний файл прошивки на моделі MCU</td><td>специфічні для цільової платформи помилки, startup, драйвери проти моделі регістрів</td><td>секунди</td></tr><tr><td><strong>Hardware in the loop</strong></td><td>справжня прошивка на справжньому MCU зі справжніми сигналами</td><td>таймінги, електричну поведінку, справжню периферію</td><td>хвилини, лабораторія</td></tr></tbody></table>
<p>Середній рівень пропускають найчастіше, хоча його додати найдешевше до CI, який уже збирає прошивку: симулятор — це виконуваний файл, який ви запускаєте з файлом, створеним вашою збіркою.</p>
<p>Інструмент у моїх експериментах — QEMU 8.2.2 з машиною <code>netduinoplus2</code>, моделлю плати з <strong>STM32F405</strong> (Cortex-M4 з картою пам'яті справжнього чипа: flash за адресою <code>0x08000000</code>, RAM за <code>0x20000000</code>, периферія за своїми справжніми адресами). Платформа має артефакт для іншого симулятора, <strong>Renode</strong>, і я повернуся до нього наприкінці.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="експеримент-1-тест-що-проходить-на-пк-і-падає-на-mcu">Експеримент 1: тест, що проходить на ПК і падає на MCU<a href="https://embedbits.cz/uk/blog/firmware-in-simulator-ci#%D0%B5%D0%BA%D1%81%D0%BF%D0%B5%D1%80%D0%B8%D0%BC%D0%B5%D0%BD%D1%82-1-%D1%82%D0%B5%D1%81%D1%82-%D1%89%D0%BE-%D0%BF%D1%80%D0%BE%D1%85%D0%BE%D0%B4%D0%B8%D1%82%D1%8C-%D0%BD%D0%B0-%D0%BF%D0%BA-%D1%96-%D0%BF%D0%B0%D0%B4%D0%B0%D1%94-%D0%BD%D0%B0-mcu" class="hash-link" aria-label="Пряме посилання на Експеримент 1: тест, що проходить на ПК і падає на MCU" title="Пряме посилання на Експеримент 1: тест, що проходить на ПК і падає на MCU" translate="no">​</a></h2>
<p>Невеликий модуль із двома функціями, які є в кожній прошивці: перетворення часу та перевірка отриманого байта.</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Conversions.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Conversions.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">long</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Conversions_Get_Microseconds</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> milliseconds</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">long</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">milliseconds </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1000L</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">bool </span><span class="token function" style="color:rgb(130, 170, 255)">Conversions_Is_HighBitSet</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> receivedByte</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">receivedByte </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Обидві функції виглядають правильно, і обидві протестовані на ПК. Вихідний код тесту однаковий для обох платформ: відрізняється лише вивід. Файл <code>Platform.h</code> друкує через <code>printf</code> на ПК і через <em>semihosting</em> (запит до симулятора або налагоджувача) на цільовій платформі, а код завершення тесту показує, чи він пройшов:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Platform.h</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifndef</span><span class="token macro property"> </span><span class="token macro property expression">PLATFORM_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">PLATFORM_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* The same test source runs on the PC and on the target: only the output differs. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">if</span><span class="token macro property"> </span><span class="token macro property expression function" style="color:rgb(130, 170, 255)">defined</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression">__arm__</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdint.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">inline</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">int</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Semihost_Call</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">int</span><span class="token plain"> operation</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">argument</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">register</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">int</span><span class="token plain">   r0 </span><span class="token function" style="color:rgb(130, 170, 255)">__asm__</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"r0"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> operation</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">register</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">r1 </span><span class="token function" style="color:rgb(130, 170, 255)">__asm__</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"r1"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> argument</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        __asm__ </span><span class="token keyword" style="font-style:italic">volatile</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"bkpt 0xAB"</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"+r"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">r0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"r"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">r1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"memory"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> r0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">inline</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Platform_Print</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token function" style="color:rgb(130, 170, 255)">Semihost_Call</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">0x04</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">inline</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Platform_Exit</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">int</span><span class="token plain"> failures</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* 0x20026: the application exited normally, 0x20023: the application failed */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token function" style="color:rgb(130, 170, 255)">Semihost_Call</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">0x18</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> failures </span><span class="token operator" style="color:rgb(137, 221, 255)">?</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0x20026u</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0x20023u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">else</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdio.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdlib.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">inline</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Platform_Print</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">fputs</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token constant" style="color:rgb(130, 170, 255)">stdout</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">inline</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Platform_Exit</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">int</span><span class="token plain"> failures</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">exit</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> failures </span><span class="token operator" style="color:rgb(137, 221, 255)">?</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><br></div></code></pre></div></div>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">tests.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdbool.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdint.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Conversions.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Platform.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">int</span><span class="token plain"> failures</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name function" style="color:rgb(130, 170, 255)">CHECK</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression">condition</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token macro property expression"> name</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token macro property expression">                                          </span><span class="token macro property punctuation" style="color:rgb(199, 146, 234)">\</span><span class="token macro property"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token macro property">    </span><span class="token macro property expression keyword" style="font-style:italic">do</span><span class="token macro property expression"> </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token macro property expression">                                                                </span><span class="token macro property punctuation" style="color:rgb(199, 146, 234)">\</span><span class="token macro property"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token macro property">        </span><span class="token macro property expression keyword" style="font-style:italic">if</span><span class="token macro property expression"> </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression">condition</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token macro property expression"> </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token macro property expression"> </span><span class="token macro property expression function" style="color:rgb(130, 170, 255)">Platform_Print</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property string" style="color:rgb(195, 232, 141)">"PASS  "</span><span class="token macro property"> </span><span class="token macro property expression">name </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"\n"</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token macro property expression"> </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token macro property expression">          </span><span class="token macro property punctuation" style="color:rgb(199, 146, 234)">\</span><span class="token macro property"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token macro property">        </span><span class="token macro property expression keyword" style="font-style:italic">else</span><span class="token macro property expression">           </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token macro property expression"> </span><span class="token macro property expression function" style="color:rgb(130, 170, 255)">Platform_Print</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property string" style="color:rgb(195, 232, 141)">"FAIL  "</span><span class="token macro property"> </span><span class="token macro property expression">name </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"\n"</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token macro property expression"> failures</span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">++</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token macro property expression"> </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token macro property expression"> </span><span class="token macro property punctuation" style="color:rgb(199, 146, 234)">\</span><span class="token macro property"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token macro property">    </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token macro property expression"> </span><span class="token macro property expression keyword" style="font-style:italic">while</span><span class="token macro property expression"> </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">0</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">int</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">main</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* 3 000 000 ms = 50 minutes = 3 000 000 000 us */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">CHECK</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">3000000000L</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Conversions_Get_Microseconds</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">3000000u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"50 minutes in microseconds"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">CHECK</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">5000L</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Conversions_Get_Microseconds</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">5u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">             </span><span class="token string" style="color:rgb(195, 232, 141)">"5 ms in microseconds"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* the byte 0xFF is received from the UART */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">CHECK</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">true </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Conversions_Is_HighBitSet</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">char</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token number" style="color:rgb(247, 140, 108)">0xFF</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">         </span><span class="token string" style="color:rgb(195, 232, 141)">"0xFF has the high bit set"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">CHECK</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">false </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Conversions_Is_HighBitSet</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">char</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token number" style="color:rgb(247, 140, 108)">0x41</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">        </span><span class="token string" style="color:rgb(195, 232, 141)">"'A' has not the high bit set"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Platform_Exit</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">failures</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> failures</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>На ПК:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">PASS  50 minutes in microseconds</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">PASS  5 ms in microseconds</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">PASS  0xFF has the high bit set</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">PASS  'A' has not the high bit set</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">exit code: 0</span><br></div></code></pre></div></div>
<p>Той самий код, зібраний <code>arm-none-eabi-gcc</code> для Cortex-M4 і запущений у QEMU:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">FAIL  50 minutes in microseconds</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">PASS  5 ms in microseconds</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">FAIL  0xFF has the high bit set</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">PASS  'A' has not the high bit set</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">exit code: 1</span><br></div></code></pre></div></div>
<p>Дві помилки, і обидві невидимі на ПК:</p>
<ul>
<li class=""><strong><code>long</code> має іншу ширину.</strong> На 64-бітному Linux-ПК <code>long</code> має 64 біти, на Cortex-M — 32. <code>3 000 000 ms * 1000</code> дорівнює <code>3 000 000 000</code>, що не вміщується в знакове 32-бітне число (найбільше — 2 147 483 647), тож результат неправильний, а в C це ще й undefined behavior.</li>
<li class=""><strong><code>char</code> має іншу знаковість.</strong> Стандарт C залишає відкритим питання, чи є звичайний <code>char</code> знаковим. На x86 є, тож <code>(char)0xFF</code> дорівнює <code>-1</code>, і перевірка <code>receivedByte &lt; 0</code> працює. На ARM він <em>беззнаковий</em>, тож <code>(char)0xFF</code> дорівнює <code>255</code>, і порівняння ніколи не буває істинним. ARM-компілятор навіть про це повідомляє, і лише там:</li>
</ul>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">Conversions.c:10:26: warning: comparison is always false due to limited range of data type [-Wtype-limits]</span><br></div></code></pre></div></div>
<p>Обидві проблеми стосуються переносимих типів, що є темою правила 4.6 у статті про <a class="" href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice">MISRA</a>: використовуйте типи із заданими розміром і знаковістю. Виправлена версія використовує <code>uint64_t</code> для часу та <code>uint8_t</code> для байта:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token class-name" style="color:rgb(255, 203, 107)">uint64_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Conversions_Get_Microseconds_Fixed</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> milliseconds</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint64_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">milliseconds </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1000u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">bool </span><span class="token function" style="color:rgb(130, 170, 255)">Conversions_Is_HighBitSet_Fixed</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> receivedByte</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">!=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">receivedByte </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0x80u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>і ті самі чотири перевірки тепер проходять <strong>на обох платформах</strong>, з кодом завершення 0 на обох. Перша версія не була помилкою тесту, і тест на ПК не був хибним: він просто тестував іншу програму.</p>
<p>Код завершення — це вся інтеграція з CI. Симулятор завершується з кодом, який прошивка передає у виклик semihosting, тож <code>add_test</code> у CMake (див. статтю про <a class="" href="https://embedbits.cz/uk/blog/cmake-for-embedded-firmware">CMake для прошивок</a>) падає, коли падає тест на цільовій платформі:</p>
<div class="language-cmake codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cmake codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">add_test</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token property">NAME</span><span class="token plain"> conversions_on_target</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    COMMAND qemu-system-arm -M netduinoplus2 -nographic</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">            -semihosting-config enable=on,target=native -kernel conversions_test.elf</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="експеримент-2-регістри-периферії">Експеримент 2: регістри периферії<a href="https://embedbits.cz/uk/blog/firmware-in-simulator-ci#%D0%B5%D0%BA%D1%81%D0%BF%D0%B5%D1%80%D0%B8%D0%BC%D0%B5%D0%BD%D1%82-2-%D1%80%D0%B5%D0%B3%D1%96%D1%81%D1%82%D1%80%D0%B8-%D0%BF%D0%B5%D1%80%D0%B8%D1%84%D0%B5%D1%80%D1%96%D1%97" class="hash-link" aria-label="Пряме посилання на Експеримент 2: регістри периферії" title="Пряме посилання на Експеримент 2: регістри периферії" translate="no">​</a></h2>
<p>Симулятор також моделює периферію, а отже, може виконуватися й код, найближчий до апаратної частини. Ось прошивка, яка надсилає текст через USART1 мікроконтролера STM32F4, написана на рівні регістрів зі справжніми адресами (без бібліотек, без startup, окрім коду зі статті про <a class="" href="https://embedbits.cz/uk/blog/before-main-startup-linker">те, що відбувається перед <code>main()</code></a>):</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">uart.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdint.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* STM32F4: RCC and USART1 registers, the same addresses as on the real chip */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">RCC_APB2ENR</span><span class="token macro property">  </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">*</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression keyword" style="font-style:italic">volatile</span><span class="token macro property expression"> </span><span class="token macro property expression class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token macro property expression"> </span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">*</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">0x40023844u</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">USART1_SR</span><span class="token macro property">    </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">*</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression keyword" style="font-style:italic">volatile</span><span class="token macro property expression"> </span><span class="token macro property expression class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token macro property expression"> </span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">*</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">0x40011000u</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">USART1_DR</span><span class="token macro property">    </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">*</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression keyword" style="font-style:italic">volatile</span><span class="token macro property expression"> </span><span class="token macro property expression class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token macro property expression"> </span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">*</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">0x40011004u</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">USART1_CR1</span><span class="token macro property">   </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">*</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression keyword" style="font-style:italic">volatile</span><span class="token macro property expression"> </span><span class="token macro property expression class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token macro property expression"> </span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">*</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">0x4001100Cu</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">RCC_APB2ENR_USART1EN</span><span class="token macro property">  </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression"> </span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">1u</span><span class="token macro property expression"> </span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">&lt;&lt;</span><span class="token macro property expression"> </span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">4</span><span class="token macro property expression"> </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">USART_CR1_UE</span><span class="token macro property">          </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression"> </span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">1u</span><span class="token macro property expression"> </span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">&lt;&lt;</span><span class="token macro property expression"> </span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">13</span><span class="token macro property expression"> </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">USART_CR1_TE</span><span class="token macro property">          </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression"> </span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">1u</span><span class="token macro property expression"> </span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">&lt;&lt;</span><span class="token macro property expression"> </span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">3</span><span class="token macro property expression"> </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">USART_SR_TXE</span><span class="token macro property">          </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression"> </span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">1u</span><span class="token macro property expression"> </span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">&lt;&lt;</span><span class="token macro property expression"> </span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">7</span><span class="token macro property expression"> </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Uart_Send</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">while</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token char" style="color:rgb(130, 170, 255)">'\0'</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">!=</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">while</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">USART1_SR </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain"> USART_SR_TXE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">     </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* wait until the data register is empty */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        USART1_DR </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">unsigned</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">char</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">text</span><span class="token operator" style="color:rgb(137, 221, 255)">++</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">int</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">main</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    RCC_APB2ENR </span><span class="token operator" style="color:rgb(137, 221, 255)">|=</span><span class="token plain"> RCC_APB2ENR_USART1EN</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">                  </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* clock of the peripheral */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    USART1_CR1  </span><span class="token operator" style="color:rgb(137, 221, 255)">|=</span><span class="token plain"> USART_CR1_UE </span><span class="token operator" style="color:rgb(137, 221, 255)">|</span><span class="token plain"> USART_CR1_TE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">           </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* enable the USART and its transmitter */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Uart_Send</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"hello from the register level\r\n"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">for</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">hello from the register level</span><br></div></code></pre></div></div>
<p>Текст з'явився в терміналі симулятора: прошивка ввімкнула тактування периферії в RCC, увімкнула USART та його передавач, дочекалася прапорця <code>TXE</code> і записала в регістр даних — точно так, як зробила б на платі, а модель USART передала байт на послідовний порт. Це тест драйвера проти моделі інтерфейсу регістрів. Якщо драйвер чекає на прапорець, який ніколи не приходить, тест знайде це за таймаутом, а не за столом з осцилографом.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="чого-симулятор-вам-не-скаже">Чого симулятор вам не скаже<a href="https://embedbits.cz/uk/blog/firmware-in-simulator-ci#%D1%87%D0%BE%D0%B3%D0%BE-%D1%81%D0%B8%D0%BC%D1%83%D0%BB%D1%8F%D1%82%D0%BE%D1%80-%D0%B2%D0%B0%D0%BC-%D0%BD%D0%B5-%D1%81%D0%BA%D0%B0%D0%B6%D0%B5" class="hash-link" aria-label="Пряме посилання на Чого симулятор вам не скаже" title="Пряме посилання на Чого симулятор вам не скаже" translate="no">​</a></h2>
<p>Симулятор — це <em>модель</em>, і кожна модель має межі. Кілька з них я зустрів в експериментах цієї серії:</p>
<ul>
<li class=""><strong>Таймінги не справжні.</strong> QEMU виконує інструкції так швидко, як дозволяє ПК, і не рахує такти справжнього ядра. Тест, який має довести, що «переривання обслуговується за 5 мікросекунд», у ньому неможливий.</li>
<li class=""><strong>Переривання приходять в інших місцях, ніж на MCU.</strong> У статті про <a class="" href="https://embedbits.cz/uk/blog/interrupts-and-main-shared-data">переривання</a> втрачене оновлення спільного лічильника не з'явилося в QEMU за 200 000 спроб, бо QEMU доставляє переривання між блоками транслюваного коду. Справжній MCU може перервати після кожної інструкції. Симулятор не доводить відсутності гонки.</li>
<li class=""><strong>Існує лише змодельована периферія.</strong> Модель STM32F405 має USART, але інший чип може мати периферію, якої в моделі немає або вона спрощена: регістри є, аналогової частини немає.</li>
<li class=""><strong>Електричного світу бракує:</strong> брязкіт контактів, шум на вході ADC, просадка напруги. Усе, що приходить ззовні, — це те, що ви самі вкладаєте в модель.</li>
</ul>
<p>Тож симулятор не замінює ні unit-тести (вони швидші й тестують логіку в ізоляції), ні апаратні тести (вони тестують реальність). Він знаходить те, що між ними: компілятор цільової платформи, типи, startup, карту пам'яті, драйвер проти моделі регістрів — і робить це за кілька секунд на кожен коміт.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="renode">Renode<a href="https://embedbits.cz/uk/blog/firmware-in-simulator-ci#renode" class="hash-link" aria-label="Пряме посилання на Renode" title="Пряме посилання на Renode" translate="no">​</a></h2>
<p>Платформа має артефакт для <strong>Renode</strong>, open-source фреймворку симуляції та віртуальної розробки від Antmicro, який постачається разом із <code>renode-test</code>, його тестовим раннером на основі Robot Framework, що використовується для інтеграційних тестів симульованої прошивки. Я не запускав його для цієї статті (реліз не вдалося завантажити в моєму середовищі), тому лише переказую те, що сказано в його документації, і не порівнюю його з QEMU. Структура такого тесту та сама, що й вище: справжній ELF, модель апаратної частини, вердикт і код завершення для CI.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="чек-лист">Чек-лист<a href="https://embedbits.cz/uk/blog/firmware-in-simulator-ci#%D1%87%D0%B5%D0%BA-%D0%BB%D0%B8%D1%81%D1%82" class="hash-link" aria-label="Пряме посилання на Чек-лист" title="Пряме посилання на Чек-лист" translate="no">​</a></h2>
<ol>
<li class="">Збирайте тест для <strong>цільової платформи</strong> компілятором цільової платформи й запускайте його в симуляторі, на додачу до ПК.</li>
<li class="">Використовуйте один вихідний код тесту для обох, з тонким шаром (<code>Platform.h</code>) для виводу та завершення.</li>
<li class="">Код завершення симулятора — це результат тесту, тож CI більше нічого не потрібно.</li>
<li class="">Використовуйте типи із заданими розміром і знаковістю (<code>uint32_t</code>, <code>uint8_t</code>) та попередження компілятора цільової платформи: другу помилку вище (<code>char</code>) знайшов би <code>-Wtype-limits</code>, якби хтось дивився на вивід ARM-збірки.</li>
<li class="">Запишіть, що симулятор довести не може (таймінги, гонки, аналоговий світ), і залиште це апаратним тестам.</li>
</ol>]]></content:encoded>
            <category>Вбудовані системи</category>
            <category>C</category>
            <category>Тестування</category>
        </item>
        <item>
            <title><![CDATA[Відтворювані збірки: зафіксуйте інструменти, зафіксуйте вихідний код, приберіть годинник]]></title>
            <link>https://embedbits.cz/uk/blog/reproducible-builds-embedded</link>
            <guid>https://embedbits.cz/uk/blog/reproducible-builds-embedded</guid>
            <pubDate>Wed, 07 Oct 2026 14:00:00 GMT</pubDate>
            <description><![CDATA[Замовник телефонує щодо firmware, яке ви поставили чотирнадцять місяців тому. Ви завантажуєте тег, збираєте, прошиваєте — і помилки немає. Помилку виправлено чи це інше firmware? Якщо ваша збірка не відтворювана, ви не можете цього сказати. Цього не може сказати ніхто, бо бінарний файл, який ви щойно зібрали, відрізняється від поставленого так, що ви не можете ні побачити, ні пояснити: інша версія компілятора, бібліотека, оновлена на ПК, шлях до теки, час доби.]]></description>
            <content:encoded><![CDATA[<p>Замовник телефонує щодо firmware, яке ви поставили чотирнадцять місяців тому. Ви завантажуєте тег, збираєте, прошиваєте — і помилки немає. Помилку виправлено чи це інше firmware? Якщо ваша збірка не <strong>відтворювана</strong>, ви не можете цього сказати. Цього не може сказати ніхто, бо бінарний файл, який ви щойно зібрали, відрізняється від поставленого так, що ви не можете ні побачити, ні пояснити: інша версія компілятора, бібліотека, оновлена на ПК, шлях до теки, час доби.</p>
<p>Відтворювана збірка має просте визначення: <strong>ті самі вхідні дані дають ті самі байти</strong>. Ця стаття показує, які вхідні дані має збірка firmware, як платформа Embedbits їх фіксує (Artifacts Handler), і експеримент, який я провів: дві збірки того самого коду в двох теках у два різні моменти дають два різні бінарні файли. А потім, після трьох змін, вони дають той самий — до останнього біта.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="вхідні-дані-збірки">Вхідні дані збірки<a href="https://embedbits.cz/uk/blog/reproducible-builds-embedded#%D0%B2%D1%85%D1%96%D0%B4%D0%BD%D1%96-%D0%B4%D0%B0%D0%BD%D1%96-%D0%B7%D0%B1%D1%96%D1%80%D0%BA%D0%B8" class="hash-link" aria-label="Пряме посилання на Вхідні дані збірки" title="Пряме посилання на Вхідні дані збірки" translate="no">​</a></h2>
<p>Збірка — це функція. Що в неї входить?</p>



































<table><thead><tr><th>Вхід</th><th>Звідки береться</th><th>Як зафіксувати</th></tr></thead><tbody><tr><td><strong>Вихідний код</strong></td><td>Git</td><td>тег або коміт, підмодулі зафіксовано їхніми комітами</td></tr><tr><td><strong>Інструменти</strong> (компілятор, лінкер, Ninja, Doxygen)</td><td>встановлені на ПК або завантажені</td><td>точні версії, з одного джерела</td></tr><tr><td><strong>Опис збірки</strong> (прапорці, linker script)</td><td>файли CMake</td><td>у репозиторії, нічого не задається вручну</td></tr><tr><td><strong>Скрипти платформи</strong></td><td>підмодуль EmBi_Platform</td><td>коміт підмодуля, оновлюється Updater свідомо</td></tr><tr><td><strong>Середовище</strong></td><td>ПК</td><td>ніщо у виході не повинно від нього залежати: шлях, час, ім'я користувача</td></tr></tbody></table>
<p>Перше, третє й четверте лежать у Git, тож зафіксовані за дизайном. Інструменти та середовище — це місця, де збірка зазвичай ламається, і кожне має власну відповідь.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="інструменти-artifacts-handler">Інструменти: Artifacts Handler<a href="https://embedbits.cz/uk/blog/reproducible-builds-embedded#%D1%96%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%B8-artifacts-handler" class="hash-link" aria-label="Пряме посилання на Інструменти: Artifacts Handler" title="Пряме посилання на Інструменти: Artifacts Handler" translate="no">​</a></h2>
<p>«Встановіть GCC 13.2 і Ninja 1.12» у README — це речення, якого ніхто не виконує буквально. Платформа розв'язує це, роблячи інструменти частиною проєкту. Artifacts Handler — це скрипт CMake, який читає перелік інструментів та їхніх версій із файлу <code>ArtifactsConfig.txt</code> і готує їх. Його документація каже, що єдине програмне забезпечення, потрібне користувачеві, — це Git і CMake, а кожен інший компонент збірки постачає handler. По кроках:</p>
<ol>
<li class=""><strong>Клонувати</strong> кореневий репозиторій артефактів (без підмодулів, щоб заощадити трафік).</li>
<li class=""><strong>Знайти</strong> в ньому артефакт (кожен інструмент — підмодуль, назви порівнюються без урахування регістру).</li>
<li class=""><strong>Перейти</strong> на запитану версію (shallow clone, <code>--depth=1</code>). Релізи позначені версією та операційною системою (<code>Win</code>, <code>Unix</code>, <code>DarwinARM</code>).</li>
<li class=""><strong>Встановити</strong> його: розпакувати архів у теку кешу.</li>
<li class=""><strong>Ініціалізувати</strong> його: здебільшого він ставить теку інструмента на <em>початок</em> <code>PATH</code>, тож інструмент проєкту перемагає будь-який інструмент, встановлений у системі.</li>
</ol>
<p>Конфігурація має один рядок на інструмент, <code>name;version;handler version</code>, наприклад рядки з документації платформи:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">ninja;1.12.1;1</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">gcc-arm-none-eabi;13.2.rel1;2</span><br></div></code></pre></div></div>
<p>Перше число — версія самого інструмента (<em>бінарного файлу</em>), друге — версія скрипта handler цього артефакту (вона може змінюватися незалежно, наприклад, коли виправляють спосіб встановлення <code>PATH</code>). Кеш має одну теку на інструмент і на версію, тож кілька версій співіснують поруч, а проєкт, якому потрібна старіша, не заважає проєкту, якому потрібна новіша.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="чому-не-latest">Чому не <code>latest</code><a href="https://embedbits.cz/uk/blog/reproducible-builds-embedded#%D1%87%D0%BE%D0%BC%D1%83-%D0%BD%D0%B5-latest" class="hash-link" aria-label="Пряме посилання на чому-не-latest" title="Пряме посилання на чому-не-latest" translate="no">​</a></h3>
<p>Handler також приймає <code>latest</code> замість числа. Це зручно й руйнує всю ідею. Я запустив handler з рядком <code>ninja;latest;latest</code>, і він надрукував, у що це перетворилося 5 жовтня 2026 року:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">-- Processing artifact ninja with Bin version latest and Core version latest.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">-- The version 1.0.0 of artifacts Core part will be used.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">-- The version 1.13.2 of artifacts Bin part will be used.</span><br></div></code></pre></div></div>
<p>(Запуск зупинився на завантаженні релізу, бо моє середовище не може дістатися GitHub API, але визначення версії відбулося.) <code>latest</code> — це <strong>рухома ціль</strong>: за документацією, перелік версій завантажується раз на добу, тож той самий файл сьогодні дає 1.13.2, а після наступного релізу — щось інше. Збірка, правильна сьогодні, завтра може бути іншою збіркою, і ніщо у вашому репозиторії про це не скаже. Тому правило просте: <strong><code>latest</code> — для спроби нової версії, а випущений проєкт фіксує кожне число.</strong></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="інші-налаштування-що-допомагають">Інші налаштування, що допомагають<a href="https://embedbits.cz/uk/blog/reproducible-builds-embedded#%D1%96%D0%BD%D1%88%D1%96-%D0%BD%D0%B0%D0%BB%D0%B0%D1%88%D1%82%D1%83%D0%B2%D0%B0%D0%BD%D0%BD%D1%8F-%D1%89%D0%BE-%D0%B4%D0%BE%D0%BF%D0%BE%D0%BC%D0%B0%D0%B3%D0%B0%D1%8E%D1%82%D1%8C" class="hash-link" aria-label="Пряме посилання на Інші налаштування, що допомагають" title="Пряме посилання на Інші налаштування, що допомагають" translate="no">​</a></h3>
<ul>
<li class=""><strong>Офлайн-режим</strong> (<code>-DOFFLINE_MODE=true</code>): перевіряється лише локальний кеш, нічого не завантажується. Це спосіб довести, що збірка не залежить від мережі, і спосіб збирати там, де мережі немає.</li>
<li class=""><strong>Розташування кешу</strong> (<code>ARTIFACTS_HANDLER_CACHE_PATH</code>) і <strong>кореневого репозиторію</strong> (<code>ARTIFACTS_HANDLER_ROOT_REPO_URL</code>) можна задати у файлі конфігурації, в середовищі або в командному рядку. Компанія може тримати власне дзеркало артефактів, тож збірка не залежить і від публічного сервера.</li>
<li class=""><strong>Контрольна сума.</strong> Кожен реліз артефакту публікується з файлом SHA-256 поруч з архівом, тож пошкоджений або підмінений файл не приймається мовчки.</li>
</ul>
<p>Інструменти — перша половина відповіді. Друга половина — те, що ви можете перевірити самі, наведеною нижче командою.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="середовище-експеримент">Середовище: експеримент<a href="https://embedbits.cz/uk/blog/reproducible-builds-embedded#%D1%81%D0%B5%D1%80%D0%B5%D0%B4%D0%BE%D0%B2%D0%B8%D1%89%D0%B5-%D0%B5%D0%BA%D1%81%D0%BF%D0%B5%D1%80%D0%B8%D0%BC%D0%B5%D0%BD%D1%82" class="hash-link" aria-label="Пряме посилання на Середовище: експеримент" title="Пряме посилання на Середовище: експеримент" translate="no">​</a></h2>
<p>Я взяв приклад проєкту зі статті про <a class="" href="https://embedbits.cz/uk/blog/cmake-for-embedded-firmware">CMake</a> і додав файл, який більшість firmware мають у тій чи іншій формі: <strong>інформацію про збірку</strong>. Він зберігає час збірки та шлях до вихідного файлу, бо колись комусь захотілося бачити у відлагоднику «коли це зібрано»:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> buildFile</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token constant" style="color:rgb(130, 170, 255)">__FILE__</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> buildTime</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token constant" style="color:rgb(130, 170, 255)">__DATE__</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">" "</span><span class="token plain"> </span><span class="token constant" style="color:rgb(130, 170, 255)">__TIME__</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>Потім я зібрав той самий код двічі: у двох різних теках (<code>a</code> і <code>b</code>), другу — на три секунди пізніше. Команда <code>strings</code>, застосована до двох бінарних файлів, показує, що всередині:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">Oct  5 2026 15:46:02</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">/tmp/.../repro/a/Application/BuildInfo.c</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">---</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">Oct  5 2026 15:46:05</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">/tmp/.../repro/b/Application/BuildInfo.c</span><br></div></code></pre></div></div>
<p>і SHA-256 двох файлів <code>firmware.bin</code> відрізняються:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">f2366319462b6fee...</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">d634c24c3c431799...</span><br></div></code></pre></div></div>
<p>Дві збірки одного коду, два різні бінарні файли. Час очевидний, але <strong>шлях</strong> — це те, що люди забувають: <code>__FILE__</code> (а також <code>assert()</code> і налагоджувальна інформація) записує в програму повний шлях до файлу, а шлях різний на ПК кожного розробника і в кожному завданні CI. Бінарний файл, зібраний у <code>/home/anna/project</code>, і той самий, зібраний у <code>/builds/job-4711/project</code>, — це два різні файли.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="три-зміни">Три зміни<a href="https://embedbits.cz/uk/blog/reproducible-builds-embedded#%D1%82%D1%80%D0%B8-%D0%B7%D0%BC%D1%96%D0%BD%D0%B8" class="hash-link" aria-label="Пряме посилання на Три зміни" title="Пряме посилання на Три зміни" translate="no">​</a></h3>
<p><strong>1. Приберіть годинник.</strong> Макросам <code>__DATE__</code> і <code>__TIME__</code> немає місця у firmware, яке має бути відтворюваним. Компілятор може це забезпечити, щоб ніхто не додав їх назад випадково:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">BuildInfo.c:5:26: error: macro "__DATE__" might prevent reproducible builds [-Werror=date-time]</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">BuildInfo.c:5:39: error: macro "__TIME__" might prevent reproducible builds [-Werror=date-time]</span><br></div></code></pre></div></div>
<p><strong>2. Версія — це вхід.</strong> Якщо ви хочете знати «що це за firmware», збірка отримує відповідь ззовні: версію, тег або хеш коміту, який система збірки передає як визначення. Той самий код має ту саму версію, отже, ті самі байти:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifndef</span><span class="token macro property"> </span><span class="token macro property expression">BUILD_VERSION</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">BUILD_VERSION</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"unknown"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> buildVersion</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> BUILD_VERSION</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p><strong>3. Приберіть шляхи.</strong> Опція <code>-ffile-prefix-map=OLD=NEW</code> замінює префікс кожного шляху, який GCC записує у вивід (<code>__FILE__</code>, налагоджувальна інформація, assertions). Тека з вихідним кодом проєкту замінюється крапкою:</p>
<div class="language-cmake codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">CMakeLists.txt (a part)</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cmake codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># The version of the firmware is an input of the build, not the time of the build.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token variable" style="color:rgb(191, 199, 213)">FIRMWARE_VERSION</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"1.2.3"</span><span class="token plain"> </span><span class="token variable" style="color:rgb(191, 199, 213)">CACHE</span><span class="token plain"> STRING </span><span class="token string" style="color:rgb(195, 232, 141)">"Version of the firmware"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">add_compile_definitions</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token variable" style="color:rgb(191, 199, 213)">BUILD_VERSION</span><span class="token plain">=</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string interpolation punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token string interpolation variable" style="color:rgb(191, 199, 213)">FIRMWARE_VERSION</span><span class="token string interpolation punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># No clock in the binary, and no path of this PC in it.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">add_compile_options</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">MCU_FLAGS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">WARNING_FLAGS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">BUILD_OPTIONS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">                    -Wdate-time -Werror=date-time</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">                    -ffile-prefix-map=</span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_SOURCE_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">=.</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><br></div></code></pre></div></div>
<p>З цими трьома змінами та сама перевірка дає:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">first : 1bedc9d2b1d78452a77fa3440851d5ed3d9f6568d96edd4c4a18be3b828dc313</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">second: 1bedc9d2b1d78452a77fa3440851d5ed3d9f6568d96edd4c4a18be3b828dc313</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">REPRODUCIBLE</span><br></div></code></pre></div></div>
<p><code>.bin</code> ідентичний, як і <code>.elf</code>, у release (<code>-Os -g0</code>) і також у debug-збірці (<code>-Og -g3</code>), чия налагоджувальна інформація повна шляхів. З <code>-ffile-prefix-map</code> <code>strings</code> бінарного файлу показує <code>./Application/BuildInfo.c</code> і версію <code>1.2.3</code>, і нічого про ПК, що його зібрав.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="тест-який-не-старіє">Тест, який не старіє<a href="https://embedbits.cz/uk/blog/reproducible-builds-embedded#%D1%82%D0%B5%D1%81%D1%82-%D1%8F%D0%BA%D0%B8%D0%B9-%D0%BD%D0%B5-%D1%81%D1%82%D0%B0%D1%80%D1%96%D1%94" class="hash-link" aria-label="Пряме посилання на Тест, який не старіє" title="Пряме посилання на Тест, який не старіє" translate="no">​</a></h2>
<p>Недостатньо зробити збірку відтворюваною один раз, бо перший же <code>__TIME__</code>, який хтось додасть, її зруйнує. Тож зробіть із цього <strong>тест</strong>: зберіть код двічі й порівняйте. Це скрипт, яким я отримав числа вище; він копіює проєкт у дві теки, збирає обидві з паузою між ними та порівнює SHA-256:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">check-reproducible.sh</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token shebang important">#!/usr/bin/env bash</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># Builds the same source twice, in two different folders, and compares the firmware.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">set</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-euo</span><span class="token plain"> pipefail</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(191, 199, 213)">source_dir</span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$(</span><span class="token string variable builtin class-name" style="color:rgb(255, 203, 107)">cd</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable string variable" style="color:rgb(191, 199, 213)">$1</span><span class="token string variable string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable operator" style="color:rgb(137, 221, 255)">&amp;&amp;</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable builtin class-name" style="color:rgb(255, 203, 107)">pwd</span><span class="token string variable" style="color:rgb(191, 199, 213)">)</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(191, 199, 213)">config</span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">${2</span><span class="token string variable operator" style="color:rgb(137, 221, 255)">:-</span><span class="token string variable" style="color:rgb(191, 199, 213)">Release}</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(191, 199, 213)">work</span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$(</span><span class="token string variable" style="color:rgb(191, 199, 213)">mktemp </span><span class="token string variable parameter variable" style="color:rgb(191, 199, 213)">-d</span><span class="token string variable" style="color:rgb(191, 199, 213)">)</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">for</span><span class="token plain"> </span><span class="token for-or-select variable" style="color:rgb(191, 199, 213)">copy</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">in</span><span class="token plain"> first second</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">do</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">cp</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-r</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$source_dir</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$work</span><span class="token string" style="color:rgb(195, 232, 141)">/</span><span class="token string variable" style="color:rgb(191, 199, 213)">$copy</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">rm</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-rf</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$work</span><span class="token string" style="color:rgb(195, 232, 141)">/</span><span class="token string variable" style="color:rgb(191, 199, 213)">$copy</span><span class="token string" style="color:rgb(195, 232, 141)">/build"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    cmake </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-S</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$work</span><span class="token string" style="color:rgb(195, 232, 141)">/</span><span class="token string variable" style="color:rgb(191, 199, 213)">$copy</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-B</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$work</span><span class="token string" style="color:rgb(195, 232, 141)">/</span><span class="token string variable" style="color:rgb(191, 199, 213)">$copy</span><span class="token string" style="color:rgb(195, 232, 141)">/build"</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-G</span><span class="token plain"> Ninja </span><span class="token punctuation" style="color:rgb(199, 146, 234)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">          </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-DCMAKE_TOOLCHAIN_FILE</span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain">cmake/arm-none-eabi.cmake </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-DCMAKE_BUILD_TYPE</span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$config</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain"> /dev/null</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    cmake </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">--build</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$work</span><span class="token string" style="color:rgb(195, 232, 141)">/</span><span class="token string variable" style="color:rgb(191, 199, 213)">$copy</span><span class="token string" style="color:rgb(195, 232, 141)">/build"</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain"> /dev/null</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">sleep</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">2</span><span class="token plain">                                    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># the second build is later and is in another folder</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">done</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(191, 199, 213)">first</span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$(</span><span class="token string variable" style="color:rgb(191, 199, 213)">sha256sum </span><span class="token string variable string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable string variable" style="color:rgb(191, 199, 213)">$work</span><span class="token string variable string" style="color:rgb(195, 232, 141)">/first/build/firmware.bin"</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable operator" style="color:rgb(137, 221, 255)">|</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable function" style="color:rgb(130, 170, 255)">cut</span><span class="token string variable" style="color:rgb(191, 199, 213)"> -d</span><span class="token string variable string" style="color:rgb(195, 232, 141)">' '</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable parameter variable" style="color:rgb(191, 199, 213)">-f1</span><span class="token string variable" style="color:rgb(191, 199, 213)">)</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(191, 199, 213)">second</span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$(</span><span class="token string variable" style="color:rgb(191, 199, 213)">sha256sum </span><span class="token string variable string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable string variable" style="color:rgb(191, 199, 213)">$work</span><span class="token string variable string" style="color:rgb(195, 232, 141)">/second/build/firmware.bin"</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable operator" style="color:rgb(137, 221, 255)">|</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable function" style="color:rgb(130, 170, 255)">cut</span><span class="token string variable" style="color:rgb(191, 199, 213)"> -d</span><span class="token string variable string" style="color:rgb(195, 232, 141)">' '</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable parameter variable" style="color:rgb(191, 199, 213)">-f1</span><span class="token string variable" style="color:rgb(191, 199, 213)">)</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"first : </span><span class="token string variable" style="color:rgb(191, 199, 213)">$first</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"second: </span><span class="token string variable" style="color:rgb(191, 199, 213)">$second</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$first</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$second</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">then</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"REPRODUCIBLE"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">else</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"NOT REPRODUCIBLE"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">exit</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">fi</span><br></div></code></pre></div></div>
<p>Запущений у CI при кожному злитті, він є вартовим. Я використав його й на зламаній версії інформації про збірку, щоб переконатися, що він може впасти, і він упав (<code>NOT REPRODUCIBLE</code>, код виходу 1, два різні хеші). Тест, який ніколи не падав, — це тест, якого ви не знаєте.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="що-ще-ламає-відтворювану-збірку">Що ще ламає відтворювану збірку<a href="https://embedbits.cz/uk/blog/reproducible-builds-embedded#%D1%89%D0%BE-%D1%89%D0%B5-%D0%BB%D0%B0%D0%BC%D0%B0%D1%94-%D0%B2%D1%96%D0%B4%D1%82%D0%B2%D0%BE%D1%80%D1%8E%D0%B2%D0%B0%D0%BD%D1%83-%D0%B7%D0%B1%D1%96%D1%80%D0%BA%D1%83" class="hash-link" aria-label="Пряме посилання на Що ще ламає відтворювану збірку" title="Пряме посилання на Що ще ламає відтворювану збірку" translate="no">​</a></h2>
<p>Я перевірив три речі вище. Далі йдуть інші добре відомі джерела проблем, про які варто пам'ятати (у прикладі вони мені не знадобилися, тож я їх лише називаю):</p>
<ul>
<li class=""><strong>Порядок файлів,</strong> що походить від <code>file(GLOB ...)</code> або зі списку теки. Вихідні файли слід перелічувати явно, як у файлах CMake модулів платформи.</li>
<li class=""><strong>Позначка часу в архіві</strong> (статична бібліотека, створена <code>ar</code>). Сучасні версії інструментів мають детермінований режим, але старий toolchain може його не мати.</li>
<li class=""><strong>Згенерований код.</strong> Моки й раннери unit-тестів або linker script, згенерований для MCU, мають генеруватися однаково з тих самих вхідних даних. Правила — сортування та жодних позначок часу у виході.</li>
<li class=""><strong>Сам компілятор.</strong> Та сама версія того самого компілятора з двох джерел (пакет дистрибутива та архів вендора) може відрізнятися бібліотеками, які підключає. Це причина брати компілятор з одного місця, у чому й полягає вся суть артефактів.</li>
<li class=""><strong>Збірка з різною кількістю потоків.</strong> Коректна система збірки дає той самий результат з <code>-j1</code> і <code>-j16</code>, і тест на це — дешеве доповнення до наведеного вище.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="чекліст">Чекліст<a href="https://embedbits.cz/uk/blog/reproducible-builds-embedded#%D1%87%D0%B5%D0%BA%D0%BB%D1%96%D1%81%D1%82" class="hash-link" aria-label="Пряме посилання на Чекліст" title="Пряме посилання на Чекліст" translate="no">​</a></h2>
<ol>
<li class="">Кожен інструмент зафіксований на точній версії в <code>ArtifactsConfig.txt</code>. Жодного <code>latest</code> у релізі.</li>
<li class="">Підмодулі (BSP, платформа) зафіксовано комітами, а оновлення — це видимий коміт.</li>
<li class="">Ніщо у firmware не залежить від часу чи шляху: <code>-Werror=date-time</code>, <code>-ffile-prefix-map</code>.</li>
<li class="">Версія — параметр збірки, виведений з тега, і зберігається у firmware.</li>
<li class="">CI збирає двічі й порівнює. Також час від часу збирає офлайн, із кешу.</li>
<li class="">Поставлений бінарний файл зберігається разом із його SHA-256 і тегом, тож на запитання «чи це той самий файл» є відповідь без перезбирання.</li>
</ol>
<p>Нагорода — речення, яке ви скажете по телефону замовнику: <em>«Я зібрав саме ваше firmware, і помилка в ньому (не) є».</em></p>]]></content:encoded>
            <category>Вбудовані системи</category>
            <category>CMake</category>
            <category>Архітектура</category>
        </item>
        <item>
            <title><![CDATA[Проєктування API периферії: вісім рішень за модулем Gpio]]></title>
            <link>https://embedbits.cz/uk/blog/designing-peripheral-api-gpio</link>
            <guid>https://embedbits.cz/uk/blog/designing-peripheral-api-gpio</guid>
            <pubDate>Wed, 07 Oct 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[GPIO — найпростіша периферія мікроконтролера як називається пін, що повертає функція, де живе полярність світлодіода. MCAL-модуль Gpio з BSP від Embedbits — реальний приклад із реальними відповідями, і я пройдуся по них одну за одною, з альтернативами та ціною.]]></description>
            <content:encoded><![CDATA[<p>GPIO — найпростіша периферія мікроконтролера: пін має високий або низький рівень. Саме тому це добра тема для статті про <strong>проєктування інтерфейсу</strong>. Тут немає апаратної складності, за якою можна сховатися, і кожне рішення — це вибір проєктувальника: як називається пін, що повертає функція, де живе полярність світлодіода. MCAL-модуль <code>Gpio</code> з BSP від Embedbits — реальний приклад із реальними відповідями, і я пройдуся по них одну за одною, з альтернативами та ціною.</p>
<p>Код модуля цитується з гілки STM32H5 репозиторію <a href="https://github.com/Embedbits/Bsp-Mcal-Gpio" target="_blank" rel="noopener noreferrer" class="">Bsp-Mcal-Gpio</a>. Приклади, що його використовують, були скомпільовані та запущені з <strong>справжніми</strong> <code>Gpio_Port.h</code> і <code>Gpio_Types.h</code> та невеликою заглушкою реалізації, щоб API можна було спробувати на ПК.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="увесь-інтерфейс-на-одному-екрані">Увесь інтерфейс на одному екрані<a href="https://embedbits.cz/uk/blog/designing-peripheral-api-gpio#%D1%83%D0%B2%D0%B5%D1%81%D1%8C-%D1%96%D0%BD%D1%82%D0%B5%D1%80%D1%84%D0%B5%D0%B9%D1%81-%D0%BD%D0%B0-%D0%BE%D0%B4%D0%BD%D0%BE%D0%BC%D1%83-%D0%B5%D0%BA%D1%80%D0%B0%D0%BD%D1%96" class="hash-link" aria-label="Пряме посилання на Увесь інтерфейс на одному екрані" title="Пряме посилання на Увесь інтерфейс на одному екрані" translate="no">​</a></h2>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_ModuleVersion_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Gpio_Get_ModuleVersion</span><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_RequestState_t</span><span class="token plain">  </span><span class="token function" style="color:rgb(130, 170, 255)">Gpio_Init</span><span class="token plain">               </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_Config_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">gpioConfig </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain">                 </span><span class="token function" style="color:rgb(130, 170, 255)">Gpio_Deinit</span><span class="token plain">             </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain">                 </span><span class="token function" style="color:rgb(130, 170, 255)">Gpio_Task</span><span class="token plain">               </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_RequestState_t</span><span class="token plain">  </span><span class="token function" style="color:rgb(130, 170, 255)">Gpio_Set_PortActive</span><span class="token plain">     </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PortId_t</span><span class="token plain"> portId </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_RequestState_t</span><span class="token plain">  </span><span class="token function" style="color:rgb(130, 170, 255)">Gpio_Set_PinMode</span><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PortId_t</span><span class="token plain"> portId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinId_t</span><span class="token plain"> pinId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinMode_t</span><span class="token plain"> pinType </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_RequestState_t</span><span class="token plain">  </span><span class="token function" style="color:rgb(130, 170, 255)">Gpio_Get_PinMode</span><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PortId_t</span><span class="token plain"> portId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinId_t</span><span class="token plain"> pinId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinMode_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> pinType </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ... speed, output type, alternate function and pull in the same pairs ... */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_RequestState_t</span><span class="token plain">  </span><span class="token function" style="color:rgb(130, 170, 255)">Gpio_Toggle_PinLevel</span><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PortId_t</span><span class="token plain"> portId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinId_t</span><span class="token plain"> pinId </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_RequestState_t</span><span class="token plain">  </span><span class="token function" style="color:rgb(130, 170, 255)">Gpio_Set_PinLevel</span><span class="token plain">       </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PortId_t</span><span class="token plain"> portId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinId_t</span><span class="token plain"> pinId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinLevel_t</span><span class="token plain"> pinLevel </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_RequestState_t</span><span class="token plain">  </span><span class="token function" style="color:rgb(130, 170, 255)">Gpio_Get_PinLevel</span><span class="token plain">       </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PortId_t</span><span class="token plain"> portId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinId_t</span><span class="token plain"> pinId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinLevel_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> pinLevel </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_RequestState_t</span><span class="token plain">  </span><span class="token function" style="color:rgb(130, 170, 255)">Gpio_Set_PinStateActive</span><span class="token plain">   </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PortId_t</span><span class="token plain"> portId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinId_t</span><span class="token plain"> pinId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinLevel_t</span><span class="token plain"> pinActiveLevel </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_RequestState_t</span><span class="token plain">  </span><span class="token function" style="color:rgb(130, 170, 255)">Gpio_Set_PinStateInactive</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PortId_t</span><span class="token plain"> portId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinId_t</span><span class="token plain"> pinId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinLevel_t</span><span class="token plain"> pinActiveLevel </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>Кожне рішення нижче можна прочитати з цього переліку.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-пін--це-пара-ідентифікаторів-а-не-макрос-вендора">1. Пін — це пара ідентифікаторів, а не макрос вендора<a href="https://embedbits.cz/uk/blog/designing-peripheral-api-gpio#1-%D0%BF%D1%96%D0%BD--%D1%86%D0%B5-%D0%BF%D0%B0%D1%80%D0%B0-%D1%96%D0%B4%D0%B5%D0%BD%D1%82%D0%B8%D1%84%D1%96%D0%BA%D0%B0%D1%82%D0%BE%D1%80%D1%96%D0%B2-%D0%B0-%D0%BD%D0%B5-%D0%BC%D0%B0%D0%BA%D1%80%D0%BE%D1%81-%D0%B2%D0%B5%D0%BD%D0%B4%D0%BE%D1%80%D0%B0" class="hash-link" aria-label="Пряме посилання на 1. Пін — це пара ідентифікаторів, а не макрос вендора" title="Пряме посилання на 1. Пін — це пара ідентифікаторів, а не макрос вендора" translate="no">​</a></h2>
<p>Драйвери вендора адресують пін за допомогою вказівника на блок регістрів і бітової маски: <code>LL_GPIO_SetOutputPin(GPIOA, LL_GPIO_PIN_5)</code>. Натомість MCAL приймає два переліки, <code>gpio_PortId_t</code> і <code>gpio_PinId_t</code>:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token function" style="color:rgb(130, 170, 255)">Gpio_Set_PinLevel</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> GPIO_PORT_A</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> GPIO_PIN_ID_5</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> GPIO_PIN_LEVEL_HIGH </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p><strong>Чому:</strong> код над MCAL не потребує жодного заголовка вендора, він не знає, що таке <code>GPIOA</code> (адреса блоку регістрів вендора), а ідентифікатор можна перевірити на діапазон, чого не можна зробити з довільним вказівником. Кожен перелік закінчується лічильником (<code>GPIO_PORT_CNT</code>, <code>GPIO_PIN_ID_CNT</code>), який є верхньою межею для перевірки на початку функції.</p>
<p><strong>Ціна:</strong> таблиця, що перетворює ідентифікатор на блок регістрів (у <code>Gpio.c</code>, один рядок на порт), і ще один рівень непрямості. Таблиця також розв'язує власну проблему: MCU одного сімейства мають різну кількість портів, тож записи загорнуто в <code>#if defined(GPIOK)</code> (див. статтю про <a class="" href="https://embedbits.cz/uk/blog/bsp-stm32-families-git-branches">сімейства</a>).</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-кожна-функція-що-може-зазнати-невдачі-повертає-стан-а-результати-виходять-через-вказівник">2. Кожна функція, що може зазнати невдачі, повертає стан, а результати виходять через вказівник<a href="https://embedbits.cz/uk/blog/designing-peripheral-api-gpio#2-%D0%BA%D0%BE%D0%B6%D0%BD%D0%B0-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D1%96%D1%8F-%D1%89%D0%BE-%D0%BC%D0%BE%D0%B6%D0%B5-%D0%B7%D0%B0%D0%B7%D0%BD%D0%B0%D1%82%D0%B8-%D0%BD%D0%B5%D0%B2%D0%B4%D0%B0%D1%87%D1%96-%D0%BF%D0%BE%D0%B2%D0%B5%D1%80%D1%82%D0%B0%D1%94-%D1%81%D1%82%D0%B0%D0%BD-%D0%B0-%D1%80%D0%B5%D0%B7%D1%83%D0%BB%D1%8C%D1%82%D0%B0%D1%82%D0%B8-%D0%B2%D0%B8%D1%85%D0%BE%D0%B4%D1%8F%D1%82%D1%8C-%D1%87%D0%B5%D1%80%D0%B5%D0%B7-%D0%B2%D0%BA%D0%B0%D0%B7%D1%96%D0%B2%D0%BD%D0%B8%D0%BA" class="hash-link" aria-label="Пряме посилання на 2. Кожна функція, що може зазнати невдачі, повертає стан, а результати виходять через вказівник" title="Пряме посилання на 2. Кожна функція, що може зазнати невдачі, повертає стан, а результати виходять через вказівник" translate="no">​</a></h2>
<p>Усі функції, окрім <code>Deinit</code>, <code>Task</code> і геттера версії, повертають <code>gpio_RequestState_t</code>, а геттери передають значення через параметр-вказівник:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinLevel_t</span><span class="token plain"> level </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> GPIO_PIN_LEVEL_LOW</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">GPIO_REQUEST_OK </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Gpio_Get_PinLevel</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">GPIO_PORT_C</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> GPIO_PIN_ID_13</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">level</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* level is valid here */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p><strong>Чому:</strong> значення, що повертає геттер, не може нести і значення, <em>і</em> інформацію про те, що значення недійсне (<code>GPIO_PIN_LEVEL_LOW</code> — дійсний рівень, тож повернений <code>0</code> неоднозначний). Зі станом у поверненому значенні та даними в параметрі недійсний порт, недійсний пін чи вказівник <code>NULL</code> — це помітна помилка, а не мовчки хибний рівень. <code>* const</code> у сигнатурі говорить, що функція не змінює вказівник, лише дані за ним.</p>
<p><strong>Ціна:</strong> два рядки замість одного при кожному використанні. А стан має два значення (<code>GPIO_REQUEST_OK</code>, <code>GPIO_REQUEST_ERROR</code>), тож викликач знає, <em>що</em> сталася помилка, але не <em>чому</em>. Для GPIO цього достатньо, бо причин лише кілька (недійсний ідентифікатор, <code>NULL</code>), і всі вони — помилки програмування. Для периферії з реальними збоями (тайм-аут I2C, помилка шини) викликачеві потрібно більше, і звичайна відповідь — багатший перелік.</p>
<p>Правило з статті про <a class="" href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice">MISRA</a>, яке тут діє, — 17.7 (повернене значення використовується): інтерфейс полегшує його дотримання, бо кожна функція, що може зазнати невдачі, повідомляє про це значенням, на яке треба подивитися.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-пін-налаштовується-даними-а-не-послідовністю-викликів">3. Пін налаштовується даними, а не послідовністю викликів<a href="https://embedbits.cz/uk/blog/designing-peripheral-api-gpio#3-%D0%BF%D1%96%D0%BD-%D0%BD%D0%B0%D0%BB%D0%B0%D1%88%D1%82%D0%BE%D0%B2%D1%83%D1%94%D1%82%D1%8C%D1%81%D1%8F-%D0%B4%D0%B0%D0%BD%D0%B8%D0%BC%D0%B8-%D0%B0-%D0%BD%D0%B5-%D0%BF%D0%BE%D1%81%D0%BB%D1%96%D0%B4%D0%BE%D0%B2%D0%BD%D1%96%D1%81%D1%82%D1%8E-%D0%B2%D0%B8%D0%BA%D0%BB%D0%B8%D0%BA%D1%96%D0%B2" class="hash-link" aria-label="Пряме посилання на 3. Пін налаштовується даними, а не послідовністю викликів" title="Пряме посилання на 3. Пін налаштовується даними, а не послідовністю викликів" translate="no">​</a></h2>
<p>Є структура конфігурації з усім, що описує пін, і одна функція, яка її застосовує:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">struct</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PortId_t</span><span class="token plain">        PortId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">         </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**&lt; GPIO port identification       */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinId_t</span><span class="token plain">         PinId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">          </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**&lt; GPIO pin identification        */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinMode_t</span><span class="token plain">       PinMode</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">        </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**&lt; GPIO pin type                  */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinPullCfg_t</span><span class="token plain">    PinPull</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">        </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**&lt; GPIO pin pull configuration    */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinSpeed_t</span><span class="token plain">      PinSpeed</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">       </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**&lt; GPIO output speed              */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinOutputType_t</span><span class="token plain"> PinOutType</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">     </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**&lt; GPIO output style              */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_AltFunction_t</span><span class="token plain">   PinAltFunction</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**&lt; Alternate function used by pin */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinLevel_t</span><span class="token plain">      PinActiveLevel</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**&lt; Pin level in active state      */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_Config_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p><strong>Чому:</strong> знання про плату тоді живе в <strong>таблиці</strong>, а код, що її застосовує, однаковий для кожної плати. Погляньте на приклад, який я скомпілював і запустив: плата — це два записи, а ініціалізація — цикл.</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">board.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdbool.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Gpio_Port.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* The board is data: which pin is what, and what "active" means for it. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">enum</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> BOARD_IO_LED</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> BOARD_IO_BUTTON</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> BOARD_IO_CNT </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">boardIo_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_Config_t</span><span class="token plain"> boardPins</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">BOARD_IO_CNT</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">BOARD_IO_LED</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">PortId </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> GPIO_PORT_A</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">PinId </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> GPIO_PIN_ID_5</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">PinMode </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> GPIO_PIN_MODE_OUTPUT</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">PinPull </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> GPIO_PIN_PULL_NONE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">PinSpeed </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> GPIO_PIN_SPEED_LOW</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">PinOutType </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> GPIO_PIN_OUTPUT_PUSHPULL</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">PinAltFunction </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> GPIO_ALT_FUNC_CNT</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">PinActiveLevel </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> GPIO_PIN_LEVEL_LOW        </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* this LED is connected to the supply: low = on */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">BOARD_IO_BUTTON</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">PortId </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> GPIO_PORT_C</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">PinId </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> GPIO_PIN_ID_13</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">PinMode </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> GPIO_PIN_MODE_INPUT</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">PinPull </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> GPIO_PIN_PULL_NONE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">PinSpeed </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> GPIO_PIN_SPEED_LOW</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">PinOutType </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> GPIO_PIN_OUTPUT_PUSHPULL</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">PinAltFunction </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> GPIO_ALT_FUNC_CNT</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">PinActiveLevel </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> GPIO_PIN_LEVEL_HIGH</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">bool </span><span class="token function" style="color:rgb(130, 170, 255)">Board_Init</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    bool isOk </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> true</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">for</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> index </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> index </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">BOARD_IO_CNT</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> index</span><span class="token operator" style="color:rgb(137, 221, 255)">++</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">GPIO_REQUEST_OK </span><span class="token operator" style="color:rgb(137, 221, 255)">!=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Gpio_Init</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">boardPins</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">index</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">            isOk </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> false</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> isOk</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">bool </span><span class="token function" style="color:rgb(130, 170, 255)">Board_Set_Led</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">bool isOn</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_Config_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">led </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">boardPins</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">BOARD_IO_LED</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">GPIO_REQUEST_OK </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">isOn</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token operator" style="color:rgb(137, 221, 255)">?</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Gpio_Set_PinStateActive</span><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">led</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">PortId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> led</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">PinId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> led</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">PinActiveLevel</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Gpio_Set_PinStateInactive</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">led</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">PortId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> led</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">PinId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> led</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">PinActiveLevel</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Оскільки таблиця — це дані, нова ревізія друкованої плати змінює рядок у таблиці й нічого в коді. В архітектурі Embedbits це завдання шару HAL: він тримає таблиці й використовує MCAL, щоб їх застосувати.</p>
<p><strong>Ціна:</strong> структура має вісім полів, і кожен запис має заповнити всі (designated initializers роблять це читабельним, а пропущене поле дорівнює нулю, що не завжди розумне значення). Тому функція <code>Get_DefaultConfig</code>, яку мають деякі інші модулі, — корисний супутник.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-полярність-сигналу--властивість-піна-а-не-коду">4. Полярність сигналу — властивість піна, а не коду<a href="https://embedbits.cz/uk/blog/designing-peripheral-api-gpio#4-%D0%BF%D0%BE%D0%BB%D1%8F%D1%80%D0%BD%D1%96%D1%81%D1%82%D1%8C-%D1%81%D0%B8%D0%B3%D0%BD%D0%B0%D0%BB%D1%83--%D0%B2%D0%BB%D0%B0%D1%81%D1%82%D0%B8%D0%B2%D1%96%D1%81%D1%82%D1%8C-%D0%BF%D1%96%D0%BD%D0%B0-%D0%B0-%D0%BD%D0%B5-%D0%BA%D0%BE%D0%B4%D1%83" class="hash-link" aria-label="Пряме посилання на 4. Полярність сигналу — властивість піна, а не коду" title="Пряме посилання на 4. Полярність сигналу — властивість піна, а не коду" translate="no">​</a></h2>
<p>Структура має поле <code>PinActiveLevel</code>, а також функції <code>Gpio_Set_PinStateActive()</code> і <code>Gpio_Set_PinStateInactive()</code>, які приймають полярність як параметр. Причина — простий факт електроніки: світлодіод можна під'єднати до землі (він світиться при <strong>високому</strong> рівні) або до живлення (він світиться при <strong>низькому</strong> рівні), а сигнал chip-select зазвичай <em>active low</em>. Якщо код каже <code>Gpio_Set_PinLevel(..., HIGH)</code> для «світлодіод увімкнено», він правильний для однієї плати й хибний для наступної.</p>
<p>З полярністю в таблиці застосунок каже «увімкнено», а таблиця — що це означає. У наведеному вище прикладі світлодіод під'єднано до живлення, тож <code>PinActiveLevel</code> — це <code>GPIO_PIN_LEVEL_LOW</code>, і тест підтверджує, що робить пін:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">after Board_Init():      the pin is high (the LED is off)</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">after Board_Set_Led(1):  the pin is low  (the LED is on)</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-переліки--це-константи-вендора">5. Переліки — це константи вендора<a href="https://embedbits.cz/uk/blog/designing-peripheral-api-gpio#5-%D0%BF%D0%B5%D1%80%D0%B5%D0%BB%D1%96%D0%BA%D0%B8--%D1%86%D0%B5-%D0%BA%D0%BE%D0%BD%D1%81%D1%82%D0%B0%D0%BD%D1%82%D0%B8-%D0%B2%D0%B5%D0%BD%D0%B4%D0%BE%D1%80%D0%B0" class="hash-link" aria-label="Пряме посилання на 5. Переліки — це константи вендора" title="Пряме посилання на 5. Переліки — це константи вендора" translate="no">​</a></h2>
<p>Типи не вигадують власних чисел, вони є числами драйвера вендора:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">enum</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    GPIO_PIN_MODE_INPUT     </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> LL_GPIO_MODE_INPUT</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">     </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**&lt; Select input mode              */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    GPIO_PIN_MODE_OUTPUT    </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> LL_GPIO_MODE_OUTPUT</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**&lt; Select output mode             */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    GPIO_PIN_MODE_ALTERNATE </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> LL_GPIO_MODE_ALTERNATE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**&lt; Select alternate function mode */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    GPIO_PIN_MODE_ANALOG    </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> LL_GPIO_MODE_ANALOG     </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**&lt; Select analog mode             */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinMode_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p><strong>Чому:</strong> перетворення з типу MCAL у значення, яке хоче функція LL, <strong>безкоштовне</strong>: немає <code>switch</code>, немає таблиці й немає способу зробити друкарську помилку в перекладі. Значення констант різняться від сімейства до сімейства, і це сховано в одному <code>#include</code> порту RAL (специфічний для сімейства <code>Stm32_gpio.h</code>).</p>
<p><strong>Ціна:</strong> <code>Gpio_Types.h</code>, публічний заголовок, включає заголовок RAL, тож константи вендора просочуються в заголовки, які бачать користувачі модуля. Альтернатива — тип із власними значеннями й таблицею перекладу у файлі <code>.c</code>, що коштує коду й місця для помилки, але тримає вендора поза публічними заголовками. Це рішення — компроміс, і добре знати, що воно таке.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-той-самий-життєвий-цикл-що-й-у-кожного-іншого-модуля">6. Той самий життєвий цикл, що й у кожного іншого модуля<a href="https://embedbits.cz/uk/blog/designing-peripheral-api-gpio#6-%D1%82%D0%BE%D0%B9-%D1%81%D0%B0%D0%BC%D0%B8%D0%B9-%D0%B6%D0%B8%D1%82%D1%82%D1%94%D0%B2%D0%B8%D0%B9-%D1%86%D0%B8%D0%BA%D0%BB-%D1%89%D0%BE-%D0%B9-%D1%83-%D0%BA%D0%BE%D0%B6%D0%BD%D0%BE%D0%B3%D0%BE-%D1%96%D0%BD%D1%88%D0%BE%D0%B3%D0%BE-%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8F" class="hash-link" aria-label="Пряме посилання на 6. Той самий життєвий цикл, що й у кожного іншого модуля" title="Пряме посилання на 6. Той самий життєвий цикл, що й у кожного іншого модуля" translate="no">​</a></h2>
<p><code>Gpio_Get_ModuleVersion()</code>, <code>Gpio_Init()</code>, <code>Gpio_Deinit()</code> і <code>Gpio_Task()</code> походять із шаблону, з якого починається кожен модуль (див. статтю про <a class="" href="https://embedbits.cz/uk/blog/file-organization-embedded-c">організацію файлів</a>). Різниця в єдиному місці, де вона має сенс: <code>Gpio_Init()</code> не <code>void</code>, вона приймає <strong>конфігурацію піна</strong>. Модуль GPIO не знає плати, тож «ініціалізувати модуль» не має сенсу без запитання «який пін?». Єдиний життєвий цикл, який гнеться там, де цього вимагає природа модуля, кращий за життєвий цикл, нав'язаний модулю, якому він не підходить.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-порядок-кроків--частина-інтерфейсу">7. Порядок кроків — частина інтерфейсу<a href="https://embedbits.cz/uk/blog/designing-peripheral-api-gpio#7-%D0%BF%D0%BE%D1%80%D1%8F%D0%B4%D0%BE%D0%BA-%D0%BA%D1%80%D0%BE%D0%BA%D1%96%D0%B2--%D1%87%D0%B0%D1%81%D1%82%D0%B8%D0%BD%D0%B0-%D1%96%D0%BD%D1%82%D0%B5%D1%80%D1%84%D0%B5%D0%B9%D1%81%D1%83" class="hash-link" aria-label="Пряме посилання на 7. Порядок кроків — частина інтерфейсу" title="Пряме посилання на 7. Порядок кроків — частина інтерфейсу" translate="no">​</a></h2>
<p>Погляньте на коментар до <code>Gpio_Init()</code>:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * Activates the port clock and configures the pin. Output level (inactive state),</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * output type, speed, pull and alternate function are configured before the pin</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * mode, so an output pin starts directly with its inactive level (no glitch).</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * Configuration stops at the first failed step.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> */</span><br></div></code></pre></div></div>
<p>Режим піна, тобто крок, який насправді <strong>підключає вихідний драйвер до виводу</strong>, — останній. Якби режим ішов першим, пін на мить видавав би рівень, який вихідний регістр мав після скидання (зазвичай низький), а потім стрибав би на правильний: імпульс на лінії, що може бути chip-select, скиданням іншої мікросхеми або затвором транзистора. Такий glitch невидимий у відлагоднику й видимий на осцилографі. Це хороший приклад правила проєктування API: <strong>коли порядок кроків важливий для апаратури, функція, що їх виконує, має володіти порядком</strong>, щоб користувач не міг його переплутати.</p>
<p>Друге речення коментаря, «stops at the first failed step», — теж проєктне рішення: функція не намагається продовжувати після помилки і повертає стан кроку, що зазнав невдачі.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-хибний-аргумент--це-помилка-яку-бачить-викликач-а-не-аварія">8. Хибний аргумент — це помилка, яку бачить викликач, а не аварія<a href="https://embedbits.cz/uk/blog/designing-peripheral-api-gpio#8-%D1%85%D0%B8%D0%B1%D0%BD%D0%B8%D0%B9-%D0%B0%D1%80%D0%B3%D1%83%D0%BC%D0%B5%D0%BD%D1%82--%D1%86%D0%B5-%D0%BF%D0%BE%D0%BC%D0%B8%D0%BB%D0%BA%D0%B0-%D1%8F%D0%BA%D1%83-%D0%B1%D0%B0%D1%87%D0%B8%D1%82%D1%8C-%D0%B2%D0%B8%D0%BA%D0%BB%D0%B8%D0%BA%D0%B0%D1%87-%D0%B0-%D0%BD%D0%B5-%D0%B0%D0%B2%D0%B0%D1%80%D1%96%D1%8F" class="hash-link" aria-label="Пряме посилання на 8. Хибний аргумент — це помилка, яку бачить викликач, а не аварія" title="Пряме посилання на 8. Хибний аргумент — це помилка, яку бачить викликач, а не аварія" translate="no">​</a></h2>
<p>Функції перевіряють свої аргументи, а відповідь — це стан:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token function" style="color:rgb(130, 170, 255)">assert</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">GPIO_REQUEST_ERROR </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Gpio_Init</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">GPIO_NULL_PTR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">assert</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">GPIO_REQUEST_ERROR </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Gpio_Get_PinLevel</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">GPIO_PORT_CNT</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> GPIO_PIN_ID_0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">level</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">assert</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">GPIO_REQUEST_ERROR </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Gpio_Set_PinLevel</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">GPIO_PORT_A</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> GPIO_PIN_ID_CNT</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> GPIO_PIN_LEVEL_HIGH</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>(Це тест моєї заглушки, але справжня <code>Gpio_Init()</code> робить те саме для <code>NULL</code>: усе тіло знаходиться всередині <code>if( GPIO_NULL_PTR != gpioConfig )</code>.) Константи <code>_CNT</code> тут мають друге завдання: <code>GPIO_PORT_CNT</code> — це перше значення, яке <strong>не</strong> є дійсним портом, тож перевірка — це одне порівняння. У firmware немає кому показати повідомлення, а <code>assert</code>, що зупиняє програму, зазвичай хибна відповідь для робочої збірки. Помилка, що піднімається до викликача, який знає, що робити (і до модуля над MCAL, який знає, що критично), — краща відповідь.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="до-чого-б-я-повернувся">До чого б я повернувся<a href="https://embedbits.cz/uk/blog/designing-peripheral-api-gpio#%D0%B4%D0%BE-%D1%87%D0%BE%D0%B3%D0%BE-%D0%B1-%D1%8F-%D0%BF%D0%BE%D0%B2%D0%B5%D1%80%D0%BD%D1%83%D0%B2%D1%81%D1%8F" class="hash-link" aria-label="Пряме посилання на До чого б я повернувся" title="Пряме посилання на До чого б я повернувся" translate="no">​</a></h2>
<p>Чесний огляд дизайну завжди має перелік, і цей має два пункти, що походять зі статті про MISRA:</p>
<ul>
<li class=""><code>Gpio_Init( gpio_Config_t *gpioConfig )</code> лише <strong>читає</strong> структуру, тож параметр міг би бути <code>const gpio_Config_t *</code> (Rule 8.13). Сигнатура говорила б, що функція не змінює конфігурацію, а таблиця плати могла б бути <code>const</code> і жити у flash замість RAM.</li>
<li class="">Двозначний <code>gpio_RequestState_t</code> однаковий у кожному модулі (його генерує шаблон). Це просте й єдине рішення, правильне для GPIO, а модулям із реальними збоями рано чи пізно знадобиться більше.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="принципи-коротко">Принципи коротко<a href="https://embedbits.cz/uk/blog/designing-peripheral-api-gpio#%D0%BF%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF%D0%B8-%D0%BA%D0%BE%D1%80%D0%BE%D1%82%D0%BA%D0%BE" class="hash-link" aria-label="Пряме посилання на Принципи коротко" title="Пряме посилання на Принципи коротко" translate="no">​</a></h2>
<ol>
<li class=""><strong>Ховайте апаратуру, а не намір.</strong> Користувач каже <em>який пін</em> і <em>яка полярність</em>, а не <em>який регістр</em>.</li>
<li class=""><strong>Робіть недійсний стан видимим.</strong> Статус для всього, що може зазнати невдачі, а вихідні дані — лише за успіху.</li>
<li class=""><strong>Кладіть знання в дані.</strong> Плата — це таблиця, яку читає цикл.</li>
<li class=""><strong>Нехай функція володіє порядком,</strong> коли апаратура про нього дбає.</li>
<li class=""><strong>Тримайте інтерфейс єдиним і чесно кажіть, де він гнеться</strong> (<code>Init</code> з параметром) та де абстракція протікає (константи вендора в типах).</li>
</ol>
<p>Інтерфейс має двох читачів: користувача сьогодні й супровідника наступного року. Наведені вище рішення написані для обох, а коментарі в заголовках — доказ того, що хтось подумав про другого.</p>]]></content:encoded>
            <category>Вбудовані системи</category>
            <category>C</category>
            <category>Архітектура</category>
        </item>
        <item>
            <title><![CDATA[Doxygen для embedded C: документація, про яку неможливо забути]]></title>
            <link>https://embedbits.cz/uk/blog/doxygen-embedded-c</link>
            <guid>https://embedbits.cz/uk/blog/doxygen-embedded-c</guid>
            <pubDate>Tue, 06 Oct 2026 20:00:00 GMT</pubDate>
            <description><![CDATA[У кожного проєкту є документація, і в кожного проєкту є документація, яка бреше. Word-файл з описом інтерфейсу був правильним у тиждень, коли його писали. Коментар над функцією чесніший, бо він за кілька рядків від коду, який описує, але його пишуть ті самі люди, які забувають. Вихід не в більшій дисципліні, а в інструменті, який читає коментарі, будує з них документацію й ламає збірку, коли чогось бракує. Цей інструмент - Doxygen.]]></description>
            <content:encoded><![CDATA[<p>У кожного проєкту є документація, і в кожного проєкту є документація, яка бреше. Word-файл з описом інтерфейсу був правильним у тиждень, коли його писали. Коментар над функцією чесніший, бо він за кілька рядків від коду, який описує, але його пишуть ті самі люди, які забувають. Вихід не в більшій дисципліні, а в <strong>інструменті, який читає коментарі, будує з них документацію й ламає збірку, коли чогось бракує</strong>. Цей інструмент - Doxygen.</p>
<p>Ця стаття показує, як у моїх проєктах виглядає задокументований модуль, як його генерують і як зробити документацію частиною CI, яку не можна оминути. Приклади зібрано з Doxygen 1.9.8 та Graphviz, а виводи - справжні.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="що-документувати-а-що-ні">Що документувати, а що ні<a href="https://embedbits.cz/uk/blog/doxygen-embedded-c#%D1%89%D0%BE-%D0%B4%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D1%83%D0%B2%D0%B0%D1%82%D0%B8-%D0%B0-%D1%89%D0%BE-%D0%BD%D1%96" class="hash-link" aria-label="Пряме посилання на Що документувати, а що ні" title="Пряме посилання на Що документувати, а що ні" translate="no">​</a></h2>
<p>У моєму <a class="" href="https://embedbits.cz/uk/docs/platform/embi-platform/coding-style">стилі кодування</a> цьому присвячено один абзац: коментарі у стилі Doxygen для всіх <strong>публічних</strong> функцій, типів і макросів, англійською, короткі й точні, <em>чому</em>, а не <em>що</em>. Кілька прикладів різниці:</p>





















<table><thead><tr><th>Коментар, який нічого не коштує</th><th>Коментар, який допомагає</th></tr></thead><tbody><tr><td><code>/* initializes the module */</code></td><td><code>Clears the filter and marks the value as not valid. The module does not report a value until Temperature_Task() has run at least once.</code></td></tr><tr><td><code>/* returns the value */</code></td><td><code>Returns TEMPERATURE_REQUEST_OK if a valid value is available, TEMPERATURE_REQUEST_ERROR if the task has not run yet.</code></td></tr><tr><td><code>/* value */</code> (для параметра)</td><td><code>Filtered temperature in tenths of a degree Celsius. Must not be NULL. Written only if the function returns TEMPERATURE_REQUEST_OK.</code></td></tr></tbody></table>
<p>Перша колонка повторює назву функції, яку читач і так бачить. Друга каже те, чого читач <strong>не бачить</strong>: одиницю виміру, допустимий діапазон, хто володіє пам'яттю, чи записується вихідне значення у випадку помилки, звідки можна викликати функцію, що треба зробити перед цим. Для embedded-інтерфейсу це також місце для інформації, яку небезпечно вгадувати: <em>чи безпечно викликати з переривання</em>, <em>як довго функція може блокувати</em>, <em>чи може вказівник бути NULL</em>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="модуль-у-файлах-платформи">Модуль у файлах платформи<a href="https://embedbits.cz/uk/blog/doxygen-embedded-c#%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C-%D1%83-%D1%84%D0%B0%D0%B9%D0%BB%D0%B0%D1%85-%D0%BF%D0%BB%D0%B0%D1%82%D1%84%D0%BE%D1%80%D0%BC%D0%B8" class="hash-link" aria-label="Пряме посилання на Модуль у файлах платформи" title="Пряме посилання на Модуль у файлах платформи" translate="no">​</a></h2>
<p>Шаблони EmBi_Platform, з яких починається кожен модуль, мають однаковий скелет документації: заголовок файлу з <code>\author</code>, <code>\file</code>, <code>\ingroup</code> та <code>\brief</code>, а над кожною функцією блок з <code>\brief</code>, описом і <code>\return</code>. Шаблон скінченного автомата додає <code>\pre</code> для передумов і блок <code>\par Used global variables</code> зі списком змінних, які функція читає (<code>in</code>), записує (<code>out</code>) або й те, й інше (<code>in,out</code>). Це напрочуд корисний вид документації: потік даних функції в трьох рядках.</p>
<p>Увесь модуль - це одна <strong>група</strong> в Doxygen. <code>\ingroup Temperature</code> у кожному файлі каже, що файл належить модулю, а сама група визначається один раз, у port-файлі:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Temperature_Port.h</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \author Mr.Nobody</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \file Temperature_Port.h</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \ingroup Temperature</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \brief Temperature module public functionality</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> *</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * The only header that other modules include.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \defgroup Temperature Temperature module</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \brief Measures the temperature and provides a filtered value.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> *</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * The module has to be initialized by Temperature_Init() and its</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * Temperature_Task() has to be called periodically. The latest filtered value</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * is read by Temperature_Get_Value().</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifndef</span><span class="token macro property"> </span><span class="token macro property expression">TEMPERATURE_TEMPERATURE_PORT_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">TEMPERATURE_TEMPERATURE_PORT_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifdef</span><span class="token macro property"> </span><span class="token macro property expression">__cplusplus</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">extern</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"C"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ============================== INCLUDES ================================== */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Temperature_Types.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ========================= EXPORTED FUNCTIONS ============================= */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \brief Initializes the module.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> *</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * Clears the filter and marks the value as not valid. The module does not</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * report a value until Temperature_Task() has run at least once.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> *</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \pre Called once, before the first Temperature_Task().</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Temperature_Init</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \brief Reads the sensor and updates the filtered value.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> *</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * Shall be called periodically from the main loop or from a scheduler, it</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * takes about the same time on every call.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> *</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \note Not safe to be called from an interrupt: the function is not reentrant.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Temperature_Task</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \brief Provides the latest filtered temperature.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> *</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \param[out] value Filtered temperature in tenths of a degree Celsius. Must</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> *                   not be NULL. It is written only if the function returns</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> *                   \ref TEMPERATURE_REQUEST_OK.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> *</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \return \ref TEMPERATURE_REQUEST_OK if a valid value is available,</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> *         \ref TEMPERATURE_REQUEST_ERROR if the task has not run yet.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token class-name" style="color:rgb(255, 203, 107)">temperature_RequestState_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Temperature_Get_Value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">temperature_Value_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> value </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifdef</span><span class="token macro property"> </span><span class="token macro property expression">__cplusplus</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><span class="token macro property"> </span><span class="token macro property comment" style="color:rgb(105, 112, 152);font-style:italic">/* TEMPERATURE_TEMPERATURE_PORT_H */</span><br></div></code></pre></div></div>
<p>Типи мають короткий опис самого типу та кожного значення (коментар <code>/**&lt; ... */</code> після члена документує цей член у тому самому рядку):</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Temperature_Types.h</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \author Mr.Nobody</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \file Temperature_Types.h</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \ingroup Temperature</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \brief Temperature module global types definition</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> *</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * This file contains the types that are used across the module and are</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * available for other modules through the port file.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifndef</span><span class="token macro property"> </span><span class="token macro property expression">TEMPERATURE_TEMPERATURE_TYPES_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">TEMPERATURE_TEMPERATURE_TYPES_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ============================== INCLUDES ================================== */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdint.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ============================== TYPEDEFS ================================== */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/** Temperature in tenths of a degree Celsius, e.g. 253 means 25.3 degC. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">int16_t</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">temperature_Value_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/** Enumeration used to signal the result of a request. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">enum</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    TEMPERATURE_REQUEST_ERROR </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**&lt; The request failed, the output is not valid */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    TEMPERATURE_REQUEST_OK          </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**&lt; The request succeeded                       */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token class-name" style="color:rgb(255, 203, 107)">temperature_RequestState_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><span class="token macro property"> </span><span class="token macro property comment" style="color:rgb(105, 112, 152);font-style:italic">/* TEMPERATURE_TEMPERATURE_TYPES_H */</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="генерація">Генерація<a href="https://embedbits.cz/uk/blog/doxygen-embedded-c#%D0%B3%D0%B5%D0%BD%D0%B5%D1%80%D0%B0%D1%86%D1%96%D1%8F" class="hash-link" aria-label="Пряме посилання на Генерація" title="Пряме посилання на Генерація" translate="no">​</a></h2>
<p>Doxygen налаштовується файлом з кількома сотнями опцій. Писати його вручну - помилка, якої легко уникнути: платформа має цей файл як шаблон (<code>Doxyfile.in</code> в артефакті Doxygen) і заповнює його зі змінних CMake, а проєкти задають лише ті, що відрізняються від типових. Те саме можливо й за допомогою власного модуля CMake, і ось уся конфігурація прикладу:</p>
<div class="language-cmake codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">CMakeLists.txt</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cmake codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">cmake_minimum_required</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token property">VERSION</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">3.19</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">project</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">DoxDemo C</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">find_package</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">Doxygen REQUIRED dot</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">DOXYGEN_PROJECT_NAME        </span><span class="token string" style="color:rgb(195, 232, 141)">"Temperature module"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">DOXYGEN_OUTPUT_DIRECTORY    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_BINARY_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/docs</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">DOXYGEN_OPTIMIZE_OUTPUT_FOR_C YES</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">DOXYGEN_FULL_PATH_NAMES     NO</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">DOXYGEN_GENERATE_LATEX      NO</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">DOXYGEN_QUIET               YES</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># The documentation is a contract: a missing description is an error.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">DOXYGEN_WARN_IF_UNDOCUMENTED     YES</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">DOXYGEN_WARN_IF_DOC_ERROR        YES</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">DOXYGEN_WARN_NO_PARAMDOC         YES</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">DOXYGEN_WARN_AS_ERROR            FAIL_ON_WARNINGS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># Graphs (the Graphviz artifact in the platform)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">DOXYGEN_HAVE_DOT                 YES</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">DOXYGEN_CALL_GRAPH               YES</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">DOXYGEN_CALLER_GRAPH             YES</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">DOXYGEN_DOT_IMAGE_FORMAT         svg</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">doxygen_add_docs</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">docs Temperature COMMENT </span><span class="token string" style="color:rgb(195, 232, 141)">"Generating the documentation"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><br></div></code></pre></div></div>
<p>а збірка - це одна ціль:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">cmake </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-S</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">.</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-B</span><span class="token plain"> build </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-G</span><span class="token plain"> Ninja</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">cmake </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">--build</span><span class="token plain"> build </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">--target</span><span class="token plain"> docs</span><br></div></code></pre></div></div>
<p>Результат - тека зі сторінками HTML. Група модуля з файлами, що до неї належать, та описом із port-файлу виглядає так:</p>
<p><img decoding="async" loading="lazy" alt="Сторінка групи Temperature у згенерованій HTML-документації" src="https://embedbits.cz/uk/assets/images/doxygen-module-group-7e9b023f433e65035665cd32e2f490ba.png" width="1000" height="900" class="img_ev3q"></p>
<p>Doxygen також малює <strong>граф включень</strong> (include graph) кожного файлу, а також <strong>граф викликів</strong> (call graph) і <strong>граф викликачів</strong> (caller graph) кожної задокументованої функції (<code>HAVE_DOT</code>, Graphviz: ось чому платформа має для нього артефакт). На графи включень варто час від часу поглядати: стрілка від файлу застосунку до внутрішнього заголовка модуля - це та сама проблема, яку описує <a class="" href="https://embedbits.cz/uk/blog/file-organization-embedded-c">стаття про організацію файлів</a>, лише побачена згори.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="документація-як-тест">Документація як тест<a href="https://embedbits.cz/uk/blog/doxygen-embedded-c#%D0%B4%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%D1%86%D1%96%D1%8F-%D1%8F%D0%BA-%D1%82%D0%B5%D1%81%D1%82" class="hash-link" aria-label="Пряме посилання на Документація як тест" title="Пряме посилання на Документація як тест" translate="no">​</a></h2>
<p>Саме ця частина змінює дисципліну. Чотири опції перетворюють відсутню документацію із «варто б» на «збірка червона»:</p>
<div class="language-cmake codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cmake codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">DOXYGEN_WARN_IF_UNDOCUMENTED   YES</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># a public member without a description</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">DOXYGEN_WARN_IF_DOC_ERROR      YES</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># a wrong tag, a parameter that does not exist</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">DOXYGEN_WARN_NO_PARAMDOC       YES</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># a function whose parameters are not described</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">DOXYGEN_WARN_AS_ERROR          FAIL_ON_WARNINGS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><br></div></code></pre></div></div>
<p>З <code>FAIL_ON_WARNINGS</code> Doxygen проходить усе, виводить <strong>усі</strong> попередження й лише потім завершується з помилкою (простий <code>YES</code> зупиняється на першому). Я спробував: видалив опис <code>Temperature_Task()</code> та <code>\param</code> у <code>Temperature_Get_Value()</code> з port-файлу, і збірка відповіла:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">Temperature/Temperature_Port.h:40: error: Member Temperature_Task(void) (function) of file Temperature_Port.h is not documented.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">Temperature/Temperature_Port.h:45: error: parameters of member Temperature_Get_Value are not documented</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">ninja: build stopped: subcommand failed.</span><br></div></code></pre></div></div>
<p>(Код завершення команди - 1, тож CI червоний.) Коли описи повернуто, та сама збірка завершується без жодного виводу й з кодом 0. Нова функція без коментаря тепер не може випадково потрапити в головну гілку, і нікому не треба бути тим, хто пам'ятає про це на рев'ю.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="що-містить-добра-документація-інтерфейсу">Що містить добра документація інтерфейсу<a href="https://embedbits.cz/uk/blog/doxygen-embedded-c#%D1%89%D0%BE-%D0%BC%D1%96%D1%81%D1%82%D0%B8%D1%82%D1%8C-%D0%B4%D0%BE%D0%B1%D1%80%D0%B0-%D0%B4%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%D1%86%D1%96%D1%8F-%D1%96%D0%BD%D1%82%D0%B5%D1%80%D1%84%D0%B5%D0%B9%D1%81%D1%83" class="hash-link" aria-label="Пряме посилання на Що містить добра документація інтерфейсу" title="Пряме посилання на Що містить добра документація інтерфейсу" translate="no">​</a></h2>
<p>Для кожної функції публічного інтерфейсу перевірте список:</p>
<ol>
<li class=""><strong>Що вона робить</strong> - одним реченням, із дієсловом (<code>\brief</code>).</li>
<li class=""><strong>Параметри</strong>: напрямок (<code>[in]</code>, <code>[out]</code>, <code>[in,out]</code>), одиниця виміру, діапазон, чи дозволено <code>NULL</code>.</li>
<li class=""><strong>Значення, що повертається</strong>: кожне можливе значення та що воно означає.</li>
<li class=""><strong>Передумови</strong> (<code>\pre</code>): що треба зробити раніше, наприклад ініціалізацію.</li>
<li class=""><strong>Контекст</strong>: чи можна викликати з переривання, чи блокує, скільки часу виконується.</li>
<li class=""><strong>Побічні ефекти</strong>: що ще вона змінює (глобальний стан, периферію).</li>
</ol>
<p>Якщо функція не має відповіді на одне з цих запитань, відповідь лежить у коді, і читач мусить його читати. Це ознака того, що інтерфейс не завершений, а вправа з написання коментаря часто й виявляється моментом, коли ви це помічаєте.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="де-це-вписується-в-платформу">Де це вписується в платформу<a href="https://embedbits.cz/uk/blog/doxygen-embedded-c#%D0%B4%D0%B5-%D1%86%D0%B5-%D0%B2%D0%BF%D0%B8%D1%81%D1%83%D1%94%D1%82%D1%8C%D1%81%D1%8F-%D0%B2-%D0%BF%D0%BB%D0%B0%D1%82%D1%84%D0%BE%D1%80%D0%BC%D1%83" class="hash-link" aria-label="Пряме посилання на Де це вписується в платформу" title="Пряме посилання на Де це вписується в платформу" translate="no">​</a></h2>
<p>Кожен модуль платформи має в своєму <code>CMakeLists.txt</code> виклик, який реєструє його теку для документації (коли артефакт Doxygen доступний у проєкті), а кореневий білд має опцію <code>DOXYGEN_ENABLED</code>. Отже, документація всього firmware генерується тією самою збіркою, що й firmware: <code>-DDOXYGEN_ENABLED=ON</code>. Артефакти (Doxygen і Graphviz) завантажуються у версії, яку зафіксував проєкт, тому сторінки сьогодні й наступного року виглядають однаково.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="чекліст">Чекліст<a href="https://embedbits.cz/uk/blog/doxygen-embedded-c#%D1%87%D0%B5%D0%BA%D0%BB%D1%96%D1%81%D1%82" class="hash-link" aria-label="Пряме посилання на Чекліст" title="Пряме посилання на Чекліст" translate="no">​</a></h2>
<ol>
<li class="">Кожна публічна функція, тип і макрос має опис. Внутрішні - лише там, де не очевидно.</li>
<li class="">Опис каже те, чого читач не бачить у сигнатурі: одиниці, діапазони, контекст, помилки.</li>
<li class="">Одна група на модуль (<code>\defgroup</code> у port-файлі, <code>\ingroup</code> в інших).</li>
<li class=""><code>WARN_IF_UNDOCUMENTED</code>, <code>WARN_NO_PARAMDOC</code> і <code>FAIL_ON_WARNINGS</code> у CI.</li>
<li class="">Графи ввімкнені, і хтось час від часу дивиться на граф включень застосунку.</li>
<li class="">Документація генерується тією самою збіркою, що й firmware, із зафіксованими інструментами.</li>
</ol>]]></content:encoded>
            <category>Вбудовані системи</category>
            <category>C</category>
            <category>CMake</category>
        </item>
        <item>
            <title><![CDATA[CMake для embedded firmware: збірка, яку можна прочитати]]></title>
            <link>https://embedbits.cz/uk/blog/cmake-for-embedded-firmware</link>
            <guid>https://embedbits.cz/uk/blog/cmake-for-embedded-firmware</guid>
            <pubDate>Tue, 06 Oct 2026 18:00:00 GMT</pubDate>
            <description><![CDATA[Проєкт в IDE - це файл, який ніхто не читає збірка - це текст, який ви читаєте, рев'юїте, версіонуєте й запускаєте в CI точнісінько так само, як і на своєму столі.]]></description>
            <content:encoded><![CDATA[<p>Проєкт в IDE - це файл, який ніхто не читає: кілька тисяч рядків XML, які IDE пише і IDE читає, і які неможливо переглянути в pull request. Збірка тоді існує лише на комп'ютері, де хтось її склікав. CMake розв'язує це іншою філософією: <strong>збірка - це текст, який ви читаєте, рев'юїте, версіонуєте й запускаєте в CI</strong> точнісінько так само, як і на своєму столі.</p>
<p>Система збірки платформи Embedbits (EmBi_Platform) зроблена на CMake, і ця стаття пояснює елементи, потрібні кожній embedded-збірці на CMake, на невеликому проєкті, який я зібрав і запустив: toolchain-файл, прапорці з іменами, типи збірки, модулі як бібліотеки, linker script, файли після лінкування та тест, що запускає firmware в симуляторі. Усі числа й виводи - справжні.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="два-шари-проєкт-і-платформа">Два шари: проєкт і платформа<a href="https://embedbits.cz/uk/blog/cmake-for-embedded-firmware#%D0%B4%D0%B2%D0%B0-%D1%88%D0%B0%D1%80%D0%B8-%D0%BF%D1%80%D0%BE%D1%94%D0%BA%D1%82-%D1%96-%D0%BF%D0%BB%D0%B0%D1%82%D1%84%D0%BE%D1%80%D0%BC%D0%B0" class="hash-link" aria-label="Пряме посилання на Два шари: проєкт і платформа" title="Пряме посилання на Два шари: проєкт і платформа" translate="no">​</a></h2>
<p>Платформа ділить збірку на дві частини, і цю ідею варто запозичити навіть для невеликого проєкту. Кореневий <code>CMakeLists.txt</code> продукту - це мінімальна точка входу, а все складне живе в окремому скрипті, який <strong>версіонується і є спільним</strong> (підмодуль Git) і який корінь підключає:</p>
<div class="language-cmake codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cmake codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">include</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_CURRENT_LIST_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/CMake/Build.cmake</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><br></div></code></pre></div></div>
<p>Скрипт платформи знає типи збірки (<code>Debug</code>, <code>Release</code>, <code>UnitTest</code>, <code>IntegrationTest</code>), перевірки параметрів, toolchain, шляхи включення та реєстрацію модулів. Продукт заповнює лише те, що специфічне для нього. Виправлення збірки - це тоді нова версія підмодуля, а не правка десяти продуктів. Приклад нижче містить обидві частини в одному проєкті для стислості, але зберігає той самий поділ: <code>cmake/</code> - це «платформа», а <code>CMakeLists.txt</code> - «продукт».</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="приклад-проєкту">Приклад проєкту<a href="https://embedbits.cz/uk/blog/cmake-for-embedded-firmware#%D0%BF%D1%80%D0%B8%D0%BA%D0%BB%D0%B0%D0%B4-%D0%BF%D1%80%D0%BE%D1%94%D0%BA%D1%82%D1%83" class="hash-link" aria-label="Пряме посилання на Приклад проєкту" title="Пряме посилання на Приклад проєкту" translate="no">​</a></h2>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">cmake/</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  arm-none-eabi.cmake       toolchain file</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  Flags.cmake               flags with names</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">Bsp/Startup/                startup code and the linker script (see the article about main())</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">Middlewares/Temperature/    a module as a library (see the article about file organization)</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">Application/                main.c</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">CMakeLists.txt</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="toolchain-файл-компілятор---це-не-pc">Toolchain-файл: компілятор - це не PC<a href="https://embedbits.cz/uk/blog/cmake-for-embedded-firmware#toolchain-%D1%84%D0%B0%D0%B9%D0%BB-%D0%BA%D0%BE%D0%BC%D0%BF%D1%96%D0%BB%D1%8F%D1%82%D0%BE%D1%80---%D1%86%D0%B5-%D0%BD%D0%B5-pc" class="hash-link" aria-label="Пряме посилання на Toolchain-файл: компілятор - це не PC" title="Пряме посилання на Toolchain-файл: компілятор - це не PC" translate="no">​</a></h2>
<p>CMake припускає, що ви збираєте для комп'ютера, за яким сидите. Toolchain-файл каже інакше:</p>
<div class="language-cmake codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">cmake/arm-none-eabi.cmake</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cmake codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># Toolchain file: tells CMake that the target is a bare-metal ARM and not the PC.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_SYSTEM_NAME</span><span class="token plain">      Generic</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_SYSTEM_PROCESSOR</span><span class="token plain"> arm</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_C_COMPILER</span><span class="token plain">   arm-none-eabi-gcc</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_ASM_COMPILER</span><span class="token plain"> arm-none-eabi-gcc</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_OBJCOPY</span><span class="token plain">      arm-none-eabi-objcopy</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_SIZE</span><span class="token plain">         arm-none-eabi-size</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># The compiler cannot link a PC executable: the check of the compiler builds a static library only.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_TRY_COMPILE_TARGET_TYPE</span><span class="token plain"> STATIC_LIBRARY</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># Search for programs on the PC, for libraries and headers only in the toolchain.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_FIND_ROOT_PATH_MODE_PROGRAM</span><span class="token plain"> NEVER</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_FIND_ROOT_PATH_MODE_LIBRARY</span><span class="token plain"> ONLY</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_FIND_ROOT_PATH_MODE_INCLUDE</span><span class="token plain"> ONLY</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><br></div></code></pre></div></div>
<p>Рядок, про який забувають, - <code>CMAKE_TRY_COMPILE_TARGET_TYPE</code>. На початку CMake збирає тестову програму, щоб перевірити, що компілятор працює, а bare-metal компілятор не може злінкувати програму для PC (немає операційної системи, немає <code>main</code>, який хтось викликає). Якщо ціллю перевірки є статична бібліотека, тест проходить. Toolchain задається в командному рядку один раз, коли створюється тека збірки:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">cmake </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-S</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">.</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-B</span><span class="token plain"> build-debug </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-G</span><span class="token plain"> Ninja </span><span class="token punctuation" style="color:rgb(199, 146, 234)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-DCMAKE_TOOLCHAIN_FILE</span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain">cmake/arm-none-eabi.cmake </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-DCMAKE_BUILD_TYPE</span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain">Debug</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">cmake </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">--build</span><span class="token plain"> build-debug</span><br></div></code></pre></div></div>
<p>Та сама тека з вихідними кодами з іншим <code>-B</code> - це інша збірка: <code>build-release</code> поруч або збірка для модульних тестів на PC, без toolchain-файлу.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="прапорці-з-іменами">Прапорці з іменами<a href="https://embedbits.cz/uk/blog/cmake-for-embedded-firmware#%D0%BF%D1%80%D0%B0%D0%BF%D0%BE%D1%80%D1%86%D1%96-%D0%B7-%D1%96%D0%BC%D0%B5%D0%BD%D0%B0%D0%BC%D0%B8" class="hash-link" aria-label="Пряме посилання на Прапорці з іменами" title="Пряме посилання на Прапорці з іменами" translate="no">​</a></h2>
<p>Рядок на кшталт <code>-mcpu=cortex-m4 -mthumb -mfloat-abi=soft -ffunction-sections ...</code> важко рев'юїти, бо ніхто не знає, яка його частина важлива. Платформа має файл <code>Flags.cmake</code> зі змінною для кожного прапорця та коментарем (<code>THUMB_MODE</code>, <code>ENABLE_ALL_WARNINGS</code>, <code>REMOVE_UNUSED_FUNCTIONS</code>, <code>ENABLE_GC_SECTIONS</code>, <code>PRINT_MEMORY_USAGE</code> і так далі, плюс варіанти CPU та FPU від Cortex-M0 до M85). Тоді проєкт читається як речення. У моєму лише те, що потрібно для прикладу:</p>
<div class="language-cmake codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">cmake/Flags.cmake</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cmake codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># Flags with names, so the CMakeLists.txt reads like a sentence.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">MCU_FLAGS             -mcpu=cortex-m4 -mthumb -mfloat-abi=soft</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">WARNING_FLAGS         -Wall -Wextra -Wshadow -Wconversion</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">SECTION_FLAGS         -ffunction-sections -fdata-sections</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">       </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># one section per function and variable...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">GC_FLAGS              -Wl,--gc-sections</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">                          </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># ...so the linker can drop the unused ones</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">STARTUP_LINK_FLAGS    -nostartfiles</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">DEBUG_OPTIONS         -Og -g3</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">RELEASE_OPTIONS       -Os -g0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cmakeliststxt">CMakeLists.txt<a href="https://embedbits.cz/uk/blog/cmake-for-embedded-firmware#cmakeliststxt" class="hash-link" aria-label="Пряме посилання на CMakeLists.txt" title="Пряме посилання на CMakeLists.txt" translate="no">​</a></h2>
<p>Увесь кореневий файл прикладу, 80 рядків:</p>
<div class="language-cmake codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">CMakeLists.txt</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cmake codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">cmake_minimum_required</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token property">VERSION</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">3.19</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># The toolchain file has to be given before project(), see the command line.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">project</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">CmakeDemo C</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">enable_testing</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">option</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">GC_SECTIONS </span><span class="token string" style="color:rgb(195, 232, 141)">"Let the linker remove the unused functions and data"</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 88, 116)">ON</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">include</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">cmake/Flags.cmake</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># CMake adds its own flags to every configuration (-O3 -DNDEBUG for Release, -g for Debug).</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># The flags of the project are in Flags.cmake, so the defaults are cleared.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_C_FLAGS_DEBUG</span><span class="token plain">   </span><span class="token string" style="color:rgb(195, 232, 141)">""</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_C_FLAGS_RELEASE</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">""</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># ---- build type: flags of the configuration ----</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">if</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">NOT</span><span class="token plain"> </span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_BUILD_TYPE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_BUILD_TYPE</span><span class="token plain"> Debug</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">endif</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">if</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_BUILD_TYPE</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">STREQUAL</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"Debug"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">BUILD_OPTIONS </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">DEBUG_OPTIONS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">elseif</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_BUILD_TYPE</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">STREQUAL</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"Release"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">BUILD_OPTIONS </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">RELEASE_OPTIONS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">else</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">message</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">FATAL_ERROR </span><span class="token string" style="color:rgb(195, 232, 141)">"Unknown build type '</span><span class="token string interpolation punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token string interpolation variable" style="color:rgb(191, 199, 213)">CMAKE_BUILD_TYPE</span><span class="token string interpolation punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token string" style="color:rgb(195, 232, 141)">', use Debug or Release"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">endif</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_C_STANDARD</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">11</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_C_STANDARD_REQUIRED</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 88, 116)">ON</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_EXPORT_COMPILE_COMMANDS</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 88, 116)">ON</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># ---- flags for everything that is built here ----</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">add_compile_options</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">MCU_FLAGS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">WARNING_FLAGS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">BUILD_OPTIONS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">if</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">GC_SECTIONS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">add_compile_options</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">SECTION_FLAGS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">endif</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">add_link_options</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">MCU_FLAGS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># ---- modules ----</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">add_subdirectory</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">Middlewares/Temperature</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># The startup code is an OBJECT library: its objects always end up in the executable,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># without relying on the linker to pull them out of an archive.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">add_library</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">Startup OBJECT Bsp/Startup/startup.c</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># GCC may turn the copy and zero loops of the startup code into calls of memcpy() and memset()</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">target_compile_options</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">Startup </span><span class="token namespace" style="color:rgb(178, 204, 214)">PRIVATE</span><span class="token plain"> -fno-tree-loop-distribute-patterns</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># ---- the firmware ----</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">LINKER_SCRIPT </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_SOURCE_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/Bsp/Startup/link.ld</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">add_executable</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">firmware.elf Application/main.c Application/Unused.c </span><span class="token punctuation" style="color:rgb(199, 146, 234)">$&lt;</span><span class="token namespace" style="color:rgb(178, 204, 214)">TARGET_OBJECTS</span><span class="token plain">:Startup</span><span class="token punctuation" style="color:rgb(199, 146, 234)">&gt;</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">target_link_libraries</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">firmware.elf </span><span class="token namespace" style="color:rgb(178, 204, 214)">PRIVATE</span><span class="token plain"> Temperature_Lib</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">target_link_options</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">firmware.elf </span><span class="token namespace" style="color:rgb(178, 204, 214)">PRIVATE</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">STARTUP_LINK_FLAGS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    -T </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">LINKER_SCRIPT</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    -Wl,-Map=firmware.map</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    -Wl,--print-memory-usage</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    -Wl,--no-warn-rwx-segments</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">if</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">GC_SECTIONS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">target_link_options</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">firmware.elf </span><span class="token namespace" style="color:rgb(178, 204, 214)">PRIVATE</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">GC_FLAGS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">endif</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set_target_properties</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">firmware.elf </span><span class="token namespace" style="color:rgb(178, 204, 214)">PROPERTIES</span><span class="token plain"> </span><span class="token property">LINK_DEPENDS</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">LINKER_SCRIPT</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># ---- after the link: the files that you flash and a size report ----</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">add_custom_command</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">TARGET firmware.elf POST_BUILD</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    COMMAND </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_OBJCOPY</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> -O binary firmware.elf firmware.bin</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    COMMAND </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_OBJCOPY</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> -O ihex   firmware.elf firmware.hex</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    COMMAND </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_SIZE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> firmware.elf</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    COMMENT </span><span class="token string" style="color:rgb(195, 232, 141)">"Creating firmware.bin, firmware.hex and the size report"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># ---- run it in the simulator as a test ----</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">find_program</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">QEMU qemu-system-arm</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">if</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">QEMU</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">add_test</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token property">NAME</span><span class="token plain"> firmware_runs_in_qemu</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        COMMAND </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">QEMU</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> -M netduinoplus2 -nographic -semihosting-config enable=on,target=native -kernel firmware.elf</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">set_tests_properties</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">firmware_runs_in_qemu </span><span class="token namespace" style="color:rgb(178, 204, 214)">PROPERTIES</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token property">PASS_REGULAR_EXPRESSION</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"temperature module works"</span><span class="token plain"> </span><span class="token property">TIMEOUT</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">10</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">endif</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><br></div></code></pre></div></div>
<p>Поясню кілька рядків, від яких залежить, чи буде firmware правильним.</p>
<p><strong>Типи збірки з явними прапорцями.</strong> Їх лише два: <code>Debug</code> (<code>-Og -g3</code>: достатньо швидко і можна налагоджувати) та <code>Release</code> (<code>-Os -g0</code>: малий розмір і без налагоджувальної інформації), а будь-що інше зупиняє конфігурацію з повідомленням про помилку. Платформа має ще два, <code>UnitTest</code> та <code>IntegrationTest</code>: у першому <code>TARGET_MCU</code> навіть не потрібен, бо збірка йде для PC.</p>
<p><strong>Типові прапорці CMake.</strong> Це пастка. CMake додає власні прапорці до кожної конфігурації, <code>-O3 -DNDEBUG</code> для <code>Release</code> і <code>-g</code> для <code>Debug</code>, перед вашими. У першій версії прикладу команда компіляції release виглядала так:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">-O3 -DNDEBUG -std=gnu11 -mcpu=cortex-m4 ... -Os -g0 -ffunction-sections ...</span><br></div></code></pre></div></div>
<p>Перемагає останній <code>-O</code>, тож це був <code>-Os</code>, але <code>-DNDEBUG</code> (який вимикає <code>assert()</code>) там був, хоча я його не просив. Якщо ви хочете знати, що збираєте, очистіть типові значення (<code>set(CMAKE_C_FLAGS_RELEASE "")</code>) і напишіть усе у власному файлі. Спосіб побачити, що справді йде до компілятора, - <code>compile_commands.json</code>, який створює опція <code>CMAKE_EXPORT_COMPILE_COMMANDS</code>: це також файл, який читають редактори та <code>clang-tidy</code>.</p>
<p><strong>Startup-код - це бібліотека <code>OBJECT</code>.</strong> Об'єкти такої бібліотеки потрапляють у виконуваний файл напряму, без покладання на те, що linker витягне їх з архіву.</p>
<p><strong>Linker script - це залежність</strong> (<code>LINK_DEPENDS</code>): коли він змінюється, firmware лінкується знову.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="модулі---це-бібліотеки">Модулі - це бібліотеки<a href="https://embedbits.cz/uk/blog/cmake-for-embedded-firmware#%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%96---%D1%86%D0%B5-%D0%B1%D1%96%D0%B1%D0%BB%D1%96%D0%BE%D1%82%D0%B5%D0%BA%D0%B8" class="hash-link" aria-label="Пряме посилання на Модулі - це бібліотеки" title="Пряме посилання на Модулі - це бібліотеки" translate="no">​</a></h2>
<p>Кожен модуль - статична бібліотека зі списком <strong>власних</strong> вихідних файлів і публічних заголовків (<a class="" href="https://embedbits.cz/uk/blog/file-organization-embedded-c">стаття про організацію файлів</a> пояснює чому). Застосунок лінкує бібліотеку і разом з нею отримує шлях включення публічних заголовків і більше нічого. У файлі CMake модуля немає <code>file(GLOB ...)</code>: вихідні файли перелічено по одному, тож новий файл - це видима зміна в pull request, а файл, випадково залишений у теці, не потрапить у firmware.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="target_mcu-одне-імя-кілька-визначень"><code>TARGET_MCU</code>: одне ім'я, кілька визначень<a href="https://embedbits.cz/uk/blog/cmake-for-embedded-firmware#target_mcu-%D0%BE%D0%B4%D0%BD%D0%B5-%D1%96%D0%BC%D1%8F-%D0%BA%D1%96%D0%BB%D1%8C%D0%BA%D0%B0-%D0%B2%D0%B8%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D1%8C" class="hash-link" aria-label="Пряме посилання на target_mcu-одне-імя-кілька-визначень" title="Пряме посилання на target_mcu-одне-імя-кілька-визначень" translate="no">​</a></h2>
<p>Збірки платформи запускаються з <code>-DTARGET_MCU=STM32G474xE</code>. Скрипт розбирає ім'я регулярним виразом і з одного параметра робить визначення для коду (<code>STM32G474xx</code> та <code>STM32G4xx</code>), які заголовки виробника використовують для вибору потрібного пристрою. Та сама логіка, запущена на чотирьох іменах:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">STM32G474xE  -&gt;  MCU_ID STM32G474xx, MCU_FAMILY_ID STM32G4xx</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">STM32U5A5xx  -&gt;  MCU_ID STM32U5A5xx, MCU_FAMILY_ID STM32U5xx</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">STM32F407VG  -&gt;  MCU_ID STM32F407xx, MCU_FAMILY_ID STM32F4xx</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">STM32H563ZI  -&gt;  MCU_ID STM32H563xx, MCU_FAMILY_ID STM32H5xx</span><br></div></code></pre></div></div>
<p>Це зв'язок зі статтею про <a class="" href="https://embedbits.cz/uk/blog/bsp-stm32-families-git-branches">родини STM32 і гілки</a>: родина вирішує, яка гілка BSP лежить у дереві, а ім'я MCU вирішує визначення та linker script.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="що-вам-каже-linker">Що вам каже linker<a href="https://embedbits.cz/uk/blog/cmake-for-embedded-firmware#%D1%89%D0%BE-%D0%B2%D0%B0%D0%BC-%D0%BA%D0%B0%D0%B6%D0%B5-linker" class="hash-link" aria-label="Пряме посилання на Що вам каже linker" title="Пряме посилання на Що вам каже linker" translate="no">​</a></h2>
<p><code>-Wl,--print-memory-usage</code> наприкінці кожного лінкування виводить, скільки кожної пам'яті використано, <code>-Wl,-Map=firmware.map</code> записує адресу всього, а крок post-build виводить розмір:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">Memory region         Used Size  Region Size  %age Used</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">           FLASH:         276 B         1 MB      0.03%</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">             RAM:           8 B       128 KB      0.01%</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   text	   data	    bss	    dec	    hex	filename</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    276	      0	      8	    284	    11c	firmware.elf</span><br></div></code></pre></div></div>
<p>Два експерименти, які я провів із цими числами, показують, чому прапорці мають бути у збірці, а не в чиїйсь голові.</p>
<p><strong>Linker може викинути те, що ніхто не викликає.</strong> З <code>-ffunction-sections -fdata-sections</code> кожна функція лежить у власній секції, а <code>-Wl,--gc-sections</code> дозволяє linker видалити ті, на які немає посилань. У прикладі є функція <code>Unused_Checksum()</code>, яку ніхто не викликає:</p>

















<table><thead><tr><th>Збірка</th><th>Розмір коду</th></tr></thead><tbody><tr><td>Release, без прапорців секцій і без <code>--gc-sections</code></td><td>324 B</td></tr><tr><td>Release, з ними (<code>-DGC_SECTIONS=ON</code>)</td><td><strong>276 B</strong></td></tr></tbody></table>
<p>Різниця у 48 байтів - це саме функція контрольної суми. У справжньому проєкті з бібліотекою виробника, яка має сотні функцій, з яких ви викликаєте десять, різниця вимірюється в кілобайтах.</p>
<p><strong>Компілятор може додати пам'ять за вашою спиною.</strong> Перша release-збірка прикладу мала <strong>772 B</strong>, майже втричі більше за debug-збірку (288 B). Map-файл показав <code>memcpy</code> і <code>memset</code> зі стандартної бібліотеки. Причина: коли оптимізація ввімкнена, GCC розпізнає цикли в startup-коді (копіювання <code>.data</code> і обнулення <code>.bss</code>) як копіювання й заповнення та замінює їх викликами цих двох функцій, які потім приходять зі своїм кодом з бібліотеки. Один прапорець на startup-об'єкті це виправляє, і результат - <strong>276 B</strong>:</p>
<div class="language-cmake codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cmake codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">target_compile_options</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">Startup </span><span class="token namespace" style="color:rgb(178, 204, 214)">PRIVATE</span><span class="token plain"> -fno-tree-loop-distribute-patterns</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><br></div></code></pre></div></div>
<p>Крім розміру, є друга причина про це дбати: startup-код виконується <em>до</em> ініціалізації пам'яті, і чим менше бібліотечного коду він використовує, тим менше може піти не так (див. статтю про <a class="" href="https://embedbits.cz/uk/blog/before-main-startup-linker">те, що відбувається перед <code>main()</code></a>).</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="після-лінкування-що-ви-прошиваєте-і-що-тестуєте">Після лінкування: що ви прошиваєте і що тестуєте<a href="https://embedbits.cz/uk/blog/cmake-for-embedded-firmware#%D0%BF%D1%96%D1%81%D0%BB%D1%8F-%D0%BB%D1%96%D0%BD%D0%BA%D1%83%D0%B2%D0%B0%D0%BD%D0%BD%D1%8F-%D1%89%D0%BE-%D0%B2%D0%B8-%D0%BF%D1%80%D0%BE%D1%88%D0%B8%D0%B2%D0%B0%D1%94%D1%82%D0%B5-%D1%96-%D1%89%D0%BE-%D1%82%D0%B5%D1%81%D1%82%D1%83%D1%94%D1%82%D0%B5" class="hash-link" aria-label="Пряме посилання на Після лінкування: що ви прошиваєте і що тестуєте" title="Пряме посилання на Після лінкування: що ви прошиваєте і що тестуєте" translate="no">​</a></h2>
<p>Команда <code>POST_BUILD</code> створює <code>firmware.bin</code> і <code>firmware.hex</code> (формати, які читають програматори) з файлу ELF і виводить розмір. Остання частина прикладу - тест: <code>add_test</code> запускає firmware в QEMU, а <code>ctest</code> перевіряє, що воно вивело очікуваний текст:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">ctest --test-dir build-debug</span><br></div></code></pre></div></div>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">1/1 Test #1: firmware_runs_in_qemu ............   Passed    0.05 sec</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">100% tests passed, 0 tests failed out of 1</span><br></div></code></pre></div></div>
<p>Це <em>smoke test</em> (чи стартує firmware і чи доходить до свого <code>main()</code>?), і він виконується за долю секунди на кожному коміті. Він не замінює тест на hardware, але збірку, що не стартує, знаходять у CI, а не за столом. Тести модулів на PC - це інша збірка того самого проєкту CMake (одну показує <a class="" href="https://embedbits.cz/uk/blog/unit-testing-unity-cmock">стаття про Unity і CMock</a>).</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="чекліст">Чекліст<a href="https://embedbits.cz/uk/blog/cmake-for-embedded-firmware#%D1%87%D0%B5%D0%BA%D0%BB%D1%96%D1%81%D1%82" class="hash-link" aria-label="Пряме посилання на Чекліст" title="Пряме посилання на Чекліст" translate="no">​</a></h2>
<ol>
<li class="">Toolchain-файл і <code>CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY</code>.</li>
<li class="">Прапорці в одному файлі з іменами, типові значення конфігурацій CMake очищені.</li>
<li class="">Кожен модуль - бібліотека з явним списком вихідних файлів і публічних заголовків.</li>
<li class=""><code>-ffunction-sections -fdata-sections</code> і <code>--gc-sections</code>, завжди.</li>
<li class=""><code>--print-memory-usage</code> і map-файл у кожному лінкуванні, після нього <code>size</code>.</li>
<li class=""><code>compile_commands.json</code> для редактора та статичного аналізу.</li>
<li class="">Linker script - залежність firmware.</li>
<li class="">Smoke test у симуляторі в CI.</li>
<li class="">Час від часу дивіться на розмір <em>release</em>-збірки. Якщо він зростає без причини, map-файл знає чому.</li>
</ol>]]></content:encoded>
            <category>Вбудовані системи</category>
            <category>CMake</category>
            <category>STM32</category>
        </item>
        <item>
            <title><![CDATA[Переривання та main(): як безпечно ділити дані]]></title>
            <link>https://embedbits.cz/uk/blog/interrupts-and-main-shared-data</link>
            <guid>https://embedbits.cz/uk/blog/interrupts-and-main-shared-data</guid>
            <pubDate>Tue, 06 Oct 2026 14:00:00 GMT</pubDate>
            <description><![CDATA[Обробник переривання та головний цикл - це дві програми, що працюють в одній пам'яті й нічого не знають одна про одну. Компілятор C не знає, що переривання існує, а CPU не знає, що дві змінні пов'язані між собою. Знає лише програміст, а помилки, які з цього випливають, найгірші з можливих: вони трапляються раз на тисячу запусків, зникають, коли підключаєш налагоджувач, і ніколи не з'являються на code review, бо код виглядає правильно.]]></description>
            <content:encoded><![CDATA[<p>Обробник переривання та головний цикл - це дві програми, що працюють в одній пам'яті й нічого не знають одна про одну. Компілятор C не знає, що переривання існує, а CPU не знає, що дві змінні пов'язані між собою. Знає лише програміст, а помилки, які з цього випливають, найгірші з можливих: вони трапляються раз на тисячу запусків, зникають, коли підключаєш налагоджувач, і ніколи не з'являються на code review, бо код виглядає правильно.</p>
<p>Ця стаття розбирає три проблеми спільних даних, одну за одною, на коді, який <strong>не працює</strong>, а потім показує шаблони, які працюють. Експерименти виконувалися на Cortex-M4 (у QEMU, на моделі плати STM32F4) або на PC, а виводи - справжні.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="проблема-1-компілятор-не-бачить-переривання">Проблема 1: компілятор не бачить переривання<a href="https://embedbits.cz/uk/blog/interrupts-and-main-shared-data#%D0%BF%D1%80%D0%BE%D0%B1%D0%BB%D0%B5%D0%BC%D0%B0-1-%D0%BA%D0%BE%D0%BC%D0%BF%D1%96%D0%BB%D1%8F%D1%82%D0%BE%D1%80-%D0%BD%D0%B5-%D0%B1%D0%B0%D1%87%D0%B8%D1%82%D1%8C-%D0%BF%D0%B5%D1%80%D0%B5%D1%80%D0%B8%D0%B2%D0%B0%D0%BD%D0%BD%D1%8F" class="hash-link" aria-label="Пряме посилання на Проблема 1: компілятор не бачить переривання" title="Пряме посилання на Проблема 1: компілятор не бачить переривання" translate="no">​</a></h2>
<p>Найпростіша комунікація між перериванням і головним циклом - прапорець. Переривання його встановлює, головний цикл на нього чекає:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">main.c (a part)</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifdef</span><span class="token macro property"> </span><span class="token macro property expression">USE_VOLATILE</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">volatile</span><span class="token plain"> bool dataReady</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">else</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> bool dataReady</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">SysTick_Handler</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    dataReady </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> true</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">int</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">main</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">SysTick_Start</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">1000u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">while</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">!</span><span class="token plain">dataReady</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* waiting for the interrupt */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Print</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"the interrupt was seen\n"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Quit</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Це вся програма, і з оптимізацією <code>-O2</code> вона <strong>ніколи не завершується</strong>. Я запустив її в QEMU з SysTick як джерелом переривання (переривання спрацьовує й встановлює прапорець) і порівняв версію з <code>volatile</code> та без нього:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">--- without volatile</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">(no output, the program was killed by the timeout after 5 s)</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">--- with volatile</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">the interrupt was seen</span><br></div></code></pre></div></div>
<p>Чому? Подивіться, що компілятор згенерував для циклу в першій версії:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain"> 8000062:  ldrb  r3, [r1, #0]       @ read the flag once</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"> 8000064:  cbnz  r3, 8000068        @ non-zero: leave the loop</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"> 8000066:  b.n   8000066            @ zero: jump to itself, forever</span><br></div></code></pre></div></div>
<p>Компілятор міркує лише про код <code>main()</code>, а в цьому коді в <code>dataReady</code> ніхто не пише. Отже, значення не може змінитися, його достатньо прочитати один раз, і цикл стає нескінченним. З його точки зору - саме те, що ви просили. З <code>volatile</code> компілятор мусить читати змінну з пам'яті на кожному проході:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain"> 8000062:  ldrb  r3, [r2, #0]       @ read the flag in every pass</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"> 8000064:  cmp   r3, #0</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"> 8000066:  beq.n 8000062</span><br></div></code></pre></div></div>
<p>Урок: <strong>кожна змінна, яку діляться переривання й основний код, має бути <code>volatile</code>.</strong> Те саме стосується кожного регістра периферії, і саме тому визначення регістрів у заголовках CMSIS є <code>volatile</code>. Пам'ятайте, що без цього в debug-збірці (<code>-O0</code>) усе працює, а в release-збірці ламається, тому помилку знаходять пізно.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="проблема-2-volatile-не-робить-операцію-атомарною">Проблема 2: <code>volatile</code> не робить операцію атомарною<a href="https://embedbits.cz/uk/blog/interrupts-and-main-shared-data#%D0%BF%D1%80%D0%BE%D0%B1%D0%BB%D0%B5%D0%BC%D0%B0-2-volatile-%D0%BD%D0%B5-%D1%80%D0%BE%D0%B1%D0%B8%D1%82%D1%8C-%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D1%96%D1%8E-%D0%B0%D1%82%D0%BE%D0%BC%D0%B0%D1%80%D0%BD%D0%BE%D1%8E" class="hash-link" aria-label="Пряме посилання на проблема-2-volatile-не-робить-операцію-атомарною" title="Пряме посилання на проблема-2-volatile-не-робить-операцію-атомарною" translate="no">​</a></h2>
<p><code>volatile</code> каже «читай і пиши пам'ять щоразу». Воно не каже «усе одразу». Невинний рядок <code>counter++</code> - це три інструкції:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">Increment_Plain:</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   ldr   r2, [pc, #8]       @ address of the counter</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   ldr   r3, [r2, #0]       @ 1. load the value</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   adds  r3, #1             @ 2. add one</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   str   r3, [r2, #0]       @ 3. store it back</span><br></div></code></pre></div></div>
<p>Якщо переривання надходить між завантаженням і записом, і переривання також інкрементує лічильник, то один із двох інкрементів <strong>губиться</strong>: основний код записує старе значення плюс один поверх значення, яке щойно записало переривання. Часова послідовність:</p>



































<table><thead><tr><th>Крок</th><th>Основний код</th><th>Переривання</th><th>Лічильник</th></tr></thead><tbody><tr><td>1</td><td>завантажує 5</td><td></td><td>5</td></tr><tr><td>2</td><td></td><td>завантажує 5, додає 1, записує 6</td><td>6</td></tr><tr><td>3</td><td>додає 1 (у регістрі має 5)</td><td></td><td>6</td></tr><tr><td>4</td><td>записує 6</td><td></td><td><strong>6, мало бути 7</strong></td></tr></tbody></table>
<p>Я хотів показати це і на Cortex-M4, але не вийшло: у 200 000 інкрементів із перериванням кожні кілька інструкцій QEMU нічого не втратив. Імовірно, тому що QEMU перевіряє переривання між блоками транслюваного коду, тож жодне ніколи не потрапляє між <code>ldr</code> і <code>str</code>. Це корисне нагадування, що <strong>симулятор не доводить відсутності race condition</strong>: справжній MCU може перервати після кожної інструкції. Той самий race у місці, де його легко відтворити, - це два потоки на PC:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">host_race.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;pthread.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdint.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdio.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">INCREMENTS</span><span class="token macro property">  </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression"> </span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">2000000u</span><span class="token macro property expression"> </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">volatile</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> plainCounter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">_Atomic</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain">  atomicCounter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token function" style="color:rgb(130, 170, 255)">Worker</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">argument</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">argument</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">for</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> index </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> index </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain"> INCREMENTS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> index</span><span class="token operator" style="color:rgb(137, 221, 255)">++</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        plainCounter</span><span class="token operator" style="color:rgb(137, 221, 255)">++</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">                       </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* load, add, store: not atomic */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token function" style="color:rgb(130, 170, 255)">atomic_fetch_add</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">atomicCounter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* one indivisible operation    */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token constant" style="color:rgb(130, 170, 255)">NULL</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">int</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">main</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">pthread_t</span><span class="token plain"> first</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> second</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">pthread_create</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">first</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token constant" style="color:rgb(130, 170, 255)">NULL</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> Worker</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token constant" style="color:rgb(130, 170, 255)">NULL</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">pthread_create</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">second</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token constant" style="color:rgb(130, 170, 255)">NULL</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> Worker</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token constant" style="color:rgb(130, 170, 255)">NULL</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">pthread_join</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">first</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token constant" style="color:rgb(130, 170, 255)">NULL</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">pthread_join</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">second</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token constant" style="color:rgb(130, 170, 255)">NULL</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">printf</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"expected        %u\n"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">2u</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> INCREMENTS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">printf</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"plain counter   %u (lost %u)\n"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> plainCounter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">2u</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> INCREMENTS </span><span class="token operator" style="color:rgb(137, 221, 255)">-</span><span class="token plain"> plainCounter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">printf</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"atomic counter  %u (lost %u)\n"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> atomicCounter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">2u</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> INCREMENTS </span><span class="token operator" style="color:rgb(137, 221, 255)">-</span><span class="token plain"> atomicCounter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">expected        4000000</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">plain counter   2652421 (lost 1347579)</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">atomic counter  4000000 (lost 0)</span><br></div></code></pre></div></div>
<p>Третина всіх інкрементів губиться, а результати відрізняються від запуску до запуску. Потік на PC і переривання на MCU в цьому сенсі - одне й те саме: другий потік керування, який може вдарити між двома інструкціями. Те саме стосується всього, що більше за слово CPU: 64-бітова часова мітка або структура з двох полів може бути прочитана наполовину старою, наполовину новою (<em>torn read</em>).</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="три-способи-розвязання">Три способи розв'язання<a href="https://embedbits.cz/uk/blog/interrupts-and-main-shared-data#%D1%82%D1%80%D0%B8-%D1%81%D0%BF%D0%BE%D1%81%D0%BE%D0%B1%D0%B8-%D1%80%D0%BE%D0%B7%D0%B2%D1%8F%D0%B7%D0%B0%D0%BD%D0%BD%D1%8F" class="hash-link" aria-label="Пряме посилання на Три способи розв'язання" title="Пряме посилання на Три способи розв'язання" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-один-записувач-на-кожну-змінну-найкращий">1. Один записувач на кожну змінну (найкращий)<a href="https://embedbits.cz/uk/blog/interrupts-and-main-shared-data#1-%D0%BE%D0%B4%D0%B8%D0%BD-%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D1%83%D0%B2%D0%B0%D1%87-%D0%BD%D0%B0-%D0%BA%D0%BE%D0%B6%D0%BD%D1%83-%D0%B7%D0%BC%D1%96%D0%BD%D0%BD%D1%83-%D0%BD%D0%B0%D0%B9%D0%BA%D1%80%D0%B0%D1%89%D0%B8%D0%B9" class="hash-link" aria-label="Пряме посилання на 1. Один записувач на кожну змінну (найкращий)" title="Пряме посилання на 1. Один записувач на кожну змінну (найкращий)" translate="no">​</a></h3>
<p>Найнадійніше рішення - спроєктувати дані так, щоб <strong>кожна змінна записувалася лише однією стороною</strong>. Переривання записує прапорець, основний код лише читає його й не скидає (або навпаки). Без двох записувачів нема чого губити. Якщо обом сторонам потрібно змінювати те саме, вони не повинні це ділити: у кожної своя змінна (лічильник переривань - <code>isrCalls</code>, лічильник основного коду - інший), а суму рахує читач.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-критична-секція">2. Критична секція<a href="https://embedbits.cz/uk/blog/interrupts-and-main-shared-data#2-%D0%BA%D1%80%D0%B8%D1%82%D0%B8%D1%87%D0%BD%D0%B0-%D1%81%D0%B5%D0%BA%D1%86%D1%96%D1%8F" class="hash-link" aria-label="Пряме посилання на 2. Критична секція" title="Пряме посилання на 2. Критична секція" translate="no">​</a></h3>
<p>Коли двох записувачів уникнути не можна, переривання вимикаються на час операції. Правильний спосіб - <strong>зберегти стан і відновити його</strong>, а не вмикати переривання наприкінці наосліп, бо функцію могли викликати з уже вимкненими перериваннями:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">Increment_CriticalSection:</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   mrs   r1, PRIMASK        @ save the state of the mask</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   cpsid i                  @ disable the interrupts</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   ldr   r3, [r2, #0]</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   adds  r3, #1</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   str   r3, [r2, #0]</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   msr   PRIMASK, r1        @ restore the state, as it was</span><br></div></code></pre></div></div>
<p>Ціна - затримка (latency): під час секції жодне переривання не може виконатися. Тому секція має бути короткою, як кілька інструкцій, і без виклику функції, циклу чи очікування всередині.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-атомарна-операція">3. Атомарна операція<a href="https://embedbits.cz/uk/blog/interrupts-and-main-shared-data#3-%D0%B0%D1%82%D0%BE%D0%BC%D0%B0%D1%80%D0%BD%D0%B0-%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D1%96%D1%8F" class="hash-link" aria-label="Пряме посилання на 3. Атомарна операція" title="Пряме посилання на 3. Атомарна операція" translate="no">​</a></h3>
<p>Для одного слова (лічильник, прапорець із лічильником) процесор має інструкції <code>LDREX</code> та <code>STREX</code>: запис вдається лише якщо ніхто не торкався адреси після завантаження, а якщо торкався - цикл повторюється. Компілятор генерує їх за вас зі стандартної операції:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">Increment_Atomic:</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   ldrex r1, [r3]           @ load and mark the address</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   adds  r1, #1</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   strex r2, r1, [r3]       @ store only if nobody has touched it, r2 = 0 on success</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   cmp   r2, #0</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">   bne.n Increment_Atomic   @ somebody did: try again</span><br></div></code></pre></div></div>
<p>у C це один рядок, <code>__atomic_fetch_add(&amp;counter, 1u, __ATOMIC_RELAXED)</code> (або <code>atomic_fetch_add()</code> з <code>&lt;stdatomic.h&gt;</code>). Він не вимикає переривання, тож не додає затримки. Він працює для однієї змінної; для двох змінних, що належать одна одній, не допомагає.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="найуживаніший-шаблон-кільцевий-буфер">Найуживаніший шаблон: кільцевий буфер<a href="https://embedbits.cz/uk/blog/interrupts-and-main-shared-data#%D0%BD%D0%B0%D0%B9%D1%83%D0%B6%D0%B8%D0%B2%D0%B0%D0%BD%D1%96%D1%88%D0%B8%D0%B9-%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD-%D0%BA%D1%96%D0%BB%D1%8C%D1%86%D0%B5%D0%B2%D0%B8%D0%B9-%D0%B1%D1%83%D1%84%D0%B5%D1%80" class="hash-link" aria-label="Пряме посилання на Найуживаніший шаблон: кільцевий буфер" title="Пряме посилання на Найуживаніший шаблон: кільцевий буфер" translate="no">​</a></h2>
<p>Найпоширеніше, що переривання передає основному коду, - потік даних: байти, прийняті UART, відліки АЦП. Переривання не може їх обробляти (воно має бути коротким), тому кладе їх у <strong>чергу</strong>, а основний код забирає їх у спокійний момент. Це задача виробник-споживач з <em>одним виробником</em> (переривання) та <em>одним споживачем</em> (основний код), і саме для цього випадку кільцевий буфер <strong>не потребує блокування</strong>:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Ring.h</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifndef</span><span class="token macro property"> </span><span class="token macro property expression">RING_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">RING_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdatomic.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdbool.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdint.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">RING_SIZE</span><span class="token macro property">  </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression"> </span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">16u</span><span class="token macro property expression"> </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token macro property expression">                       </span><span class="token macro property comment" style="color:rgb(105, 112, 152);font-style:italic">/* has to be a power of two */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">RING_MASK</span><span class="token macro property">  </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression"> RING_SIZE </span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">-</span><span class="token macro property expression"> </span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">1u</span><span class="token macro property expression"> </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/*</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * Single producer, single consumer ring buffer without any lock.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * head: written only by the producer, tail: written only by the consumer.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * Both are free-running counters, the difference is the number of items.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">struct</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain">         items</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">RING_SIZE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">atomic_uint_fast32_t</span><span class="token plain"> head</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">atomic_uint_fast32_t</span><span class="token plain"> tail</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token class-name" style="color:rgb(255, 203, 107)">ring_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">inline</span><span class="token plain"> bool </span><span class="token function" style="color:rgb(130, 170, 255)">Ring_Push</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">ring_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">ring</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> item</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint_fast32_t</span><span class="token plain"> head </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">atomic_load_explicit</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">ring</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">head</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> memory_order_relaxed</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint_fast32_t</span><span class="token plain"> tail </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">atomic_load_explicit</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">ring</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">tail</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> memory_order_acquire</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">RING_SIZE </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">head </span><span class="token operator" style="color:rgb(137, 221, 255)">-</span><span class="token plain"> tail</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> false</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">                            </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* full, the producer decides what to do */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    ring</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">items</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">head </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain"> RING_MASK</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> item</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">atomic_store_explicit</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">ring</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">head</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> head </span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> memory_order_release</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* publish the item */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> true</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">inline</span><span class="token plain"> bool </span><span class="token function" style="color:rgb(130, 170, 255)">Ring_Pop</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">ring_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">ring</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">item</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint_fast32_t</span><span class="token plain"> tail </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">atomic_load_explicit</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">ring</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">tail</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> memory_order_relaxed</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint_fast32_t</span><span class="token plain"> head </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">atomic_load_explicit</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">ring</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">head</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> memory_order_acquire</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">head </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> tail</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> false</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">                            </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* empty */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">item </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> ring</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">items</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">tail </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain"> RING_MASK</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">atomic_store_explicit</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">ring</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">tail</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> tail </span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> memory_order_release</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* free the slot */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> true</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><br></div></code></pre></div></div>
<p>Чому це правильно?</p>
<ul>
<li class=""><code>head</code> записує <strong>лише виробник</strong>, а <code>tail</code> - <strong>лише споживач</strong>. Немає змінної з двома записувачами, тож жодне оновлення не може загубитися: це перше рішення вище, вбудоване в структуру.</li>
<li class="">Лічильники вільно біжать (free-running) і ніколи не скидаються. Кількість елементів - це <code>head - tail</code>, і завдяки беззнаковій арифметиці вона правильна навіть після переповнення лічильників. Якщо розмір - степінь двійки, позиція отримується простою маскою замість ділення.</li>
<li class=""><strong>Порядок</strong> має значення: виробник записує елемент і <strong>після цього</strong> публікує його зсувом <code>head</code> (<code>memory_order_release</code>), а споживач спершу читає <code>head</code> (<code>memory_order_acquire</code>) і лише потім елемент. На одному ядрі Cortex-M важливий лише порядок, у якому компілятор записує, і атомарні операції його зберігають. На MCU з двома ядрами (деякі STM32H7 мають два) або з кешем той самий код усе ще правильний, і це причина писати його в такій формі, а не з <code>volatile</code>, який «якось працює».</li>
<li class="">Виробник не чекає. Повний буфер означає «відкинь елемент і порахуй це», бо переривання не може ні на кого чекати. Лічильник відкинутих у тесті каже вам, що буфер замалий.</li>
</ul>
<p>Я перевірив це двома способами. На PC з двома потоками, виробником і споживачем, пропускаємо 20 мільйонів елементів через буфер на 16 слотів:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">items 20000000, order errors 0</span><br></div></code></pre></div></div>
<p>і на Cortex-M4 у QEMU: переривання SysTick - виробник, а <code>main()</code> - споживач, 5000 елементів:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">items received  5000</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">order errors    0</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">full buffer hits 0</span><br></div></code></pre></div></div>
<p>Перший тест жорсткіший (два потоки справді виконуються паралельно на двох ядрах), другий - справжня ситуація MCU. Це також ідеальний кандидат для модульного тесту на PC, як у статті про <a class="" href="https://embedbits.cz/uk/blog/unit-testing-unity-cmock">модульне тестування з Unity та CMock</a>: модуль не залежить від hardware.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="правила-для-обробників-переривань">Правила для обробників переривань<a href="https://embedbits.cz/uk/blog/interrupts-and-main-shared-data#%D0%BF%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%B0-%D0%B4%D0%BB%D1%8F-%D0%BE%D0%B1%D1%80%D0%BE%D0%B1%D0%BD%D0%B8%D0%BA%D1%96%D0%B2-%D0%BF%D0%B5%D1%80%D0%B5%D1%80%D0%B8%D0%B2%D0%B0%D0%BD%D1%8C" class="hash-link" aria-label="Пряме посилання на Правила для обробників переривань" title="Пряме посилання на Правила для обробників переривань" translate="no">​</a></h2>
<ol>
<li class=""><strong>Короткі.</strong> Прочитати регістр, зберегти дані, встановити прапорець, скинути pending-прапорець, повернутися. Обробка виконується в основному коді.</li>
<li class=""><strong>Без очікування, без виділення пам'яті, без <code>printf</code>.</strong> Нічого, що блокує, триває довго або викликає функцію, небезпечну для виклику з переривання (вона <em>нереентерабельна</em>, якщо всередині використовує статичний буфер).</li>
<li class=""><strong>Усе спільне - <code>volatile</code> або атомарне</strong>, і в кожної змінної визначений записувач.</li>
<li class=""><strong>Не діліть більше, ніж потрібно.</strong> Прапорець або черга між сторонами, а не структура з десяти полів.</li>
<li class=""><strong>Думайте про пріоритет.</strong> Переривання вищого пріоритету може перервати інше переривання. Змінна, спільна для двох переривань, має ту саму проблему, що й спільна з основним кодом.</li>
</ol>
<p>У Embedbits BSP модулі периферії (наприклад, USART) мають варіанти обробки даних polling, interrupt і DMA, і варіант з перериваннями - саме це: обробник на рівні MCAL, який бере дані з периферії та передає їх застосунку через буфер, тоді як <code>Task</code> модуля працює в головному циклі й виконує обробку.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="підсумок">Підсумок<a href="https://embedbits.cz/uk/blog/interrupts-and-main-shared-data#%D0%BF%D1%96%D0%B4%D1%81%D1%83%D0%BC%D0%BE%D0%BA" class="hash-link" aria-label="Пряме посилання на Підсумок" title="Пряме посилання на Підсумок" translate="no">​</a></h2>



































<table><thead><tr><th>Проблема</th><th>Симптом</th><th>Лікування</th></tr></thead><tbody><tr><td>Компілятор не бачить переривання</td><td>Працює з <code>-O0</code>, нескінченний цикл з <code>-O2</code></td><td><code>volatile</code> на кожній спільній змінній</td></tr><tr><td><code>counter++</code> не атомарний</td><td>Лічильник, який іноді замалий</td><td>Один записувач на змінну, критична секція або <code>atomic_fetch_add</code></td></tr><tr><td>Більше за слово</td><td>Наполовину старе, наполовину нове значення</td><td>Критична секція, або порядковий номер, або черга</td></tr><tr><td>Потік даних</td><td>Втрачені або змішані байти</td><td>Кільцевий буфер з одним виробником і одним споживачем</td></tr><tr><td>Тест проходить у симуляторі</td><td>Помилка в полі</td><td>Пам'ятайте, що симулятор перериває в інших місцях, ніж MCU</td></tr></tbody></table>
<p>Три види коду в цій статті мали одну спільну рису: усі вони <em>виглядали</em> правильно.</p>]]></content:encoded>
            <category>Вбудовані системи</category>
            <category>C</category>
            <category>STM32</category>
        </item>
        <item>
            <title><![CDATA[Скінченні автомати на практиці: кнопка з debounce та довгим натисканням]]></title>
            <link>https://embedbits.cz/uk/blog/fsm-in-practice-button</link>
            <guid>https://embedbits.cz/uk/blog/fsm-in-practice-button</guid>
            <pubDate>Tue, 06 Oct 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[У першій статті про скінченні автомати я дав вам шаблон і закінчив словами «далі буде». Це продовження, а найкращий спосіб продовжити теорію — це задача. Я обрав ту, що є в кожному embedded-проєкті і яку ніхто не робить правильно з першого разу: кнопка.]]></description>
            <content:encoded><![CDATA[<p>У <a class="" href="https://embedbits.cz/uk/blog/finite-state-machines">першій статті про скінченні автомати</a> я дав вам шаблон і закінчив словами «далі буде». Це продовження, а найкращий спосіб продовжити теорію — це задача. Я обрав ту, що є в кожному embedded-проєкті і яку ніхто не робить правильно з першого разу: <strong>кнопка</strong>.</p>
<p>Кнопка виглядає тривіальною: пін, високий або низький рівень. Але механічний контакт <em>деренчить</em> (bounce), тож пін робить 1 0 1 1 0 1, перш ніж заспокоїться, а продукт зазвичай хоче від тієї самої кнопки двох різних речей: коротке і довге натискання. Якщо написати це з прапорцями та лічильниками в головному циклі, вийде кілька <code>if</code>, що залежать один від одного так, що за місяць ніхто не зможе пояснити як. Скінченний автомат вирішує це так, що пояснити можна таблицею.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="шаблон-в-одному-абзаці">Шаблон в одному абзаці<a href="https://embedbits.cz/uk/blog/fsm-in-practice-button#%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD-%D0%B2-%D0%BE%D0%B4%D0%BD%D0%BE%D0%BC%D1%83-%D0%B0%D0%B1%D0%B7%D0%B0%D1%86%D1%96" class="hash-link" aria-label="Пряме посилання на Шаблон в одному абзаці" title="Пряме посилання на Шаблон в одному абзаці" translate="no">​</a></h2>
<p>Кожен стан має чотири підпрограми, і кожна з них має рівно одне завдання:</p>






























<table><thead><tr><th>Підпрограма</th><th>Коли викликається</th><th>Завдання</th></tr></thead><tbody><tr><td><code>Entry</code></td><td>один раз, коли стан входить в дію</td><td>підготувати стан (скинути лічильник, створити подію)</td></tr><tr><td><code>Execute</code></td><td>при кожному запуску автомата, поки він у цьому стані</td><td>виконати роботу стану (рахувати, семплювати)</td></tr><tr><td><code>CheckLeave</code></td><td>при кожному запуску, після <code>Execute</code></td><td>вирішити, чи залишати стан і куди переходити: <strong>єдине місце, де запитується перехід</strong></td></tr><tr><td><code>Leave</code></td><td>один раз, коли стан залишається</td><td>прибрати за собою</td></tr></tbody></table>
<p>Ядро автомата запускає <code>Execute</code> і <code>CheckLeave</code> поточного стану, а коли стан змінився, запускає <code>Leave</code> старого і <code>Entry</code> нового. Підпрограми ніколи не викликають одна одну, а код автомата не містить нічого, крім автомата. Якщо хочете знати, чому пристрій у певному стані, читайте <code>CheckLeave</code> попереднього.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="спочатку-дизайн-стани-та-переходи">Спочатку дизайн: стани та переходи<a href="https://embedbits.cz/uk/blog/fsm-in-practice-button#%D1%81%D0%BF%D0%BE%D1%87%D0%B0%D1%82%D0%BA%D1%83-%D0%B4%D0%B8%D0%B7%D0%B0%D0%B9%D0%BD-%D1%81%D1%82%D0%B0%D0%BD%D0%B8-%D1%82%D0%B0-%D0%BF%D0%B5%D1%80%D0%B5%D1%85%D0%BE%D0%B4%D0%B8" class="hash-link" aria-label="Пряме посилання на Спочатку дизайн: стани та переходи" title="Пряме посилання на Спочатку дизайн: стани та переходи" translate="no">​</a></h2>
<p>Задача запускається кожні 10 мс і читає сирий контакт. Час debounce — 50 мс, а межа довгого натискання — 1 секунда. П'яти станів достатньо:</p>





















































<table><thead><tr><th>Стан</th><th>Execute</th><th>Залишається, коли</th><th>Куди</th></tr></thead><tbody><tr><td><code>RELEASED</code></td><td>нічого</td><td>контакт замкнений</td><td><code>DEBOUNCE_PRESS</code></td></tr><tr><td><code>DEBOUNCE_PRESS</code></td><td>рахує тики</td><td>контакт знову розімкнувся (це був збій)</td><td><code>RELEASED</code></td></tr><tr><td></td><td></td><td>контакт був замкнений 50 мс</td><td><code>PRESSED</code></td></tr><tr><td><code>PRESSED</code></td><td>рахує тики</td><td>контакт розмикається: подія <strong>короткого натискання</strong></td><td><code>DEBOUNCE_RELEASE</code></td></tr><tr><td></td><td></td><td>контакт замкнений 1 с</td><td><code>LONG_PRESS</code></td></tr><tr><td><code>LONG_PRESS</code></td><td>нічого, подію <strong>довгого натискання</strong> створено на вході</td><td>контакт розмикається</td><td><code>DEBOUNCE_RELEASE</code></td></tr><tr><td><code>DEBOUNCE_RELEASE</code></td><td>рахує тики розімкненого контакту, деренчання скидає лічильник</td><td>контакт був розімкнений 50 мс</td><td><code>RELEASED</code></td></tr></tbody></table>
<p>Складання таблиці — це справжня робота. Коли вона повна, код — лише вправа з друкування. Коли ні (що відбувається в <code>LONG_PRESS</code>, коли контакт деренчить при відпусканні?), таблиця покаже діру ще до того, як ви напишете рядок C.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="код">Код<a href="https://embedbits.cz/uk/blog/fsm-in-practice-button#%D0%BA%D0%BE%D0%B4" class="hash-link" aria-label="Пряме посилання на Код" title="Пряме посилання на Код" translate="no">​</a></h2>
<p>Вхід приходить із BSP, тож модуль не має апаратних залежностей і його можна тестувати на ПК (той самий <em>seam</em>, що й у статті про <a class="" href="https://embedbits.cz/uk/blog/unit-testing-unity-cmock">unit-тестування</a>). Порт модуля — це <code>Init</code>, <code>Task</code> і функція, що повертає подію один раз:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Button_Port.h</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifndef</span><span class="token macro property"> </span><span class="token macro property expression">BUTTON_BUTTON_PORT_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">BUTTON_BUTTON_PORT_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">enum</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    BUTTON_EVENT_NONE </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    BUTTON_EVENT_SHORT_PRESS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    BUTTON_EVENT_LONG_PRESS</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token class-name" style="color:rgb(255, 203, 107)">button_Event_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain">           </span><span class="token function" style="color:rgb(130, 170, 255)">Button_Init</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain">           </span><span class="token function" style="color:rgb(130, 170, 255)">Button_Task</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">          </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* call it every 10 ms */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token class-name" style="color:rgb(255, 203, 107)">button_Event_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_Get_Event</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">     </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* returns the event once and clears it */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><br></div></code></pre></div></div>
<p>Типи слідують шаблону. Є одна зміна, яку я зробив навмисно: таблиця підпрограм <strong>індексується станом</strong>, а не обшукується, і перелік закінчується <code>BUTTON_STATE_COUNT</code>, який має два застосування.</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Button.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">enum</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    BUTTON_STATE_RELEASED </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**&lt; Contact open, waiting for a press      */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    BUTTON_STATE_DEBOUNCE_PRESS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**&lt; Contact closed, is it stable?          */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    BUTTON_STATE_PRESSED</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">          </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**&lt; Stable press, short or long?           */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    BUTTON_STATE_LONG_PRESS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">       </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**&lt; Held longer than the limit             */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    BUTTON_STATE_DEBOUNCE_RELEASE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**&lt; Contact open again, is it stable?      */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    BUTTON_STATE_COUNT             </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**&lt; Number of the states, keep it the last */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token class-name" style="color:rgb(255, 203, 107)">button_SM_States_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token class-name" style="color:rgb(255, 203, 107)">button_SM_PtrToRoutine_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">struct</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">button_SM_PtrToRoutine_t</span><span class="token plain"> entry</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">button_SM_PtrToRoutine_t</span><span class="token plain"> execute</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">button_SM_PtrToRoutine_t</span><span class="token plain"> checkLeave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">button_SM_PtrToRoutine_t</span><span class="token plain"> leave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token class-name" style="color:rgb(255, 203, 107)">button_SM_Routines_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>Перше застосування — сама таблиця. З designated initializers позиція рядка не має значення, тож стан не можна прив'язати до неправильних підпрограм, коли хтось додасть рядок посередині (шаблон потребує коментаря «цей масив має мати той самий порядок, що й enum станів»):</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Button.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* The table is indexed by the state, so the order of the lines does not matter. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">button_SM_Routines_t</span><span class="token plain"> stateRoutines</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">BUTTON_STATE_COUNT</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">BUTTON_STATE_RELEASED</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain">         </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> Button_Released_Entry</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">        Button_Released_Execute</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">                                        Button_Released_CheckLeave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">   Button_Released_Leave        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">BUTTON_STATE_DEBOUNCE_PRESS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain">   </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> Button_DebouncePress_Entry</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">   Button_DebouncePress_Execute</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">                                        Button_DebouncePress_CheckLeave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> Button_DebouncePress_Leave </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">BUTTON_STATE_PRESSED</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain">          </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> Button_Pressed_Entry</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">         Button_Pressed_Execute</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">                                        Button_Pressed_CheckLeave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">    Button_Pressed_Leave         </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">BUTTON_STATE_LONG_PRESS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain">       </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> Button_LongPress_Entry</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">       Button_LongPress_Execute</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">                                        Button_LongPress_CheckLeave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">  Button_LongPress_Leave       </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">BUTTON_STATE_DEBOUNCE_RELEASE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> Button_DebounceRelease_Entry</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> Button_DebounceRelease_Execute</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">                                        Button_DebounceRelease_CheckLeave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> Button_DebounceRelease_Leave </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>Друге застосування — ядро автомата, в якому та сама константа замінює жорстко закодований «останній стан» у перевірці діапазону:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Button.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_HandleStateTransition</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">BUTTON_STATE_COUNT </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;=</span><span class="token plain"> newState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        newState </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> BUTTON_STATE_RELEASED</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">      </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* invalid request: go to the safe state */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    stateRoutines</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">actualState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">execute</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    stateRoutines</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">actualState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">checkLeave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">actualState </span><span class="token operator" style="color:rgb(137, 221, 255)">!=</span><span class="token plain"> newState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        stateRoutines</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">actualState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">leave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        actualState </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> newState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        stateRoutines</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">actualState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">entry</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Зверніть увагу, чого ядро <em>не знає</em>: кнопок, часу, подій. Тепер стани. Більшість підпрограм — однорядкові:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Button.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ---- RELEASED ---- */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_Released_Entry</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> stateTicks </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_Released_Execute</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_Released_CheckLeave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">isContactClosed</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> newState </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> BUTTON_STATE_DEBOUNCE_PRESS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_Released_Leave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ---- DEBOUNCE_PRESS: closed, but is it only a bounce? ---- */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_DebouncePress_Entry</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> stateTicks </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_DebouncePress_Execute</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> stateTicks</span><span class="token operator" style="color:rgb(137, 221, 255)">++</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_DebouncePress_CheckLeave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">!</span><span class="token plain">isContactClosed</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        newState </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> BUTTON_STATE_RELEASED</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">               </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* a glitch, nothing happened */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">else</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">BUTTON_DEBOUNCE_TICKS </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;=</span><span class="token plain"> stateTicks</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        newState </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> BUTTON_STATE_PRESSED</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_DebouncePress_Leave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ---- PRESSED: a short press if released early, a long one if held ---- */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_Pressed_Entry</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> stateTicks </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_Pressed_Execute</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> stateTicks</span><span class="token operator" style="color:rgb(137, 221, 255)">++</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_Pressed_CheckLeave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">!</span><span class="token plain">isContactClosed</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        pendingEvent </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> BUTTON_EVENT_SHORT_PRESS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        newState     </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> BUTTON_STATE_DEBOUNCE_RELEASE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">else</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">BUTTON_LONG_PRESS_TICKS </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;=</span><span class="token plain"> stateTicks</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        newState </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> BUTTON_STATE_LONG_PRESS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_Pressed_Leave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ---- LONG_PRESS: the event is created once, on the entry ---- */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_LongPress_Entry</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> pendingEvent </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> BUTTON_EVENT_LONG_PRESS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_LongPress_Execute</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_LongPress_CheckLeave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">!</span><span class="token plain">isContactClosed</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> newState </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> BUTTON_STATE_DEBOUNCE_RELEASE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_LongPress_Leave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ---- DEBOUNCE_RELEASE: open, has to stay open for the debounce time ---- */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_DebounceRelease_Entry</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> stateTicks </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_DebounceRelease_Execute</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> stateTicks </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> isContactClosed </span><span class="token operator" style="color:rgb(137, 221, 255)">?</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint16_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">stateTicks </span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_DebounceRelease_CheckLeave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">BUTTON_DEBOUNCE_TICKS </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;=</span><span class="token plain"> stateTicks</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> newState </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> BUTTON_STATE_RELEASED</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_DebounceRelease_Leave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Кілька деталей, на які варто глянути:</p>
<ul>
<li class=""><code>Button_Task()</code> читає вхід <strong>один раз</strong> за запуск і зберігає його в <code>isContactClosed</code>. Усі підпрограми одного запуску бачать те саме значення, навіть якщо пін змінюється посеред запуску.</li>
<li class="">Подія короткого натискання створюється в <code>Button_Pressed_CheckLeave()</code>, у тому самому місці, де ухвалюється рішення. Подія довгого натискання створюється в <code>Button_LongPress_Entry()</code>: entry викликається рівно один раз на перехід, тож подія не може бути створена двічі, що б не робив контакт, поки кнопка утримується.</li>
<li class="">Debounce відпускання (<code>DEBOUNCE_RELEASE</code>) скидає свій лічильник щоразу, коли контакт знову замикається. Це відповідь на деренчання при відпусканні: автомат чекає, доки контакт не вгамується на 50 мс.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="тест">Тест<a href="https://embedbits.cz/uk/blog/fsm-in-practice-button#%D1%82%D0%B5%D1%81%D1%82" class="hash-link" aria-label="Пряме посилання на Тест" title="Пряме посилання на Тест" translate="no">​</a></h2>
<p>Тест замінює <code>Bsp_Get_ButtonRaw()</code> функцією, що читає рядок: один символ на тик у 10 мс, <code>1</code> — замкнений контакт. Шаблони — це історія кнопки, а результат — рядок подій, <code>S</code> для короткого натискання і <code>L</code> для довгого:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">test_Button.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* The fake input: one character per 10 ms tick, '1' = contact closed. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">inputPattern</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain">      inputIndex</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">bool </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Get_ButtonRaw</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> symbol </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> inputPattern</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">inputIndex</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token char" style="color:rgb(130, 170, 255)">'\0'</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">!=</span><span class="token plain"> symbol</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> inputIndex</span><span class="token operator" style="color:rgb(137, 221, 255)">++</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token char" style="color:rgb(130, 170, 255)">'1'</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> symbol</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* Runs the machine over the whole pattern and returns the events as a string, S = short, L = long. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token function" style="color:rgb(130, 170, 255)">Run</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">pattern</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> events</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token number" style="color:rgb(247, 140, 108)">16</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> count </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    inputPattern </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> pattern</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    inputIndex   </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">memset</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">events</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">sizeof</span><span class="token plain"> events</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Button_Init</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">for</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> tick </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> tick </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">strlen</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">pattern</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">20u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> tick</span><span class="token operator" style="color:rgb(137, 221, 255)">++</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* 20 extra ticks of an open contact */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token function" style="color:rgb(130, 170, 255)">Button_Task</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">button_Event_t</span><span class="token plain"> event </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Button_Get_Event</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">BUTTON_EVENT_SHORT_PRESS </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> event</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> events</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">count</span><span class="token operator" style="color:rgb(137, 221, 255)">++</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token char" style="color:rgb(130, 170, 255)">'S'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">BUTTON_EVENT_LONG_PRESS  </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> event</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> events</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">count</span><span class="token operator" style="color:rgb(137, 221, 255)">++</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token char" style="color:rgb(130, 170, 255)">'L'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> events</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Expect</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">pattern</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">expected</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">result </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Run</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">pattern</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">printf</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"%-34s -&gt; %s\n"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token char" style="color:rgb(130, 170, 255)">'\0'</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> result</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">?</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"(no event)"</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> result</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">assert</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">strcmp</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">expected</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> result</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">int</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">main</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> longHold</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token number" style="color:rgb(247, 140, 108)">220</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Expect</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"nothing happens"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">                 </span><span class="token string" style="color:rgb(195, 232, 141)">"0000000000"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">                              </span><span class="token string" style="color:rgb(195, 232, 141)">""</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Expect</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"a 20 ms glitch"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">                  </span><span class="token string" style="color:rgb(195, 232, 141)">"0011000000"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">                              </span><span class="token string" style="color:rgb(195, 232, 141)">""</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Expect</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"a clean 200 ms press"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">            </span><span class="token string" style="color:rgb(195, 232, 141)">"00111111111111111111110000000000"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">        </span><span class="token string" style="color:rgb(195, 232, 141)">"S"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Expect</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"a press with a bouncing contact"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"0101101111111111111111011010000000000"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">   </span><span class="token string" style="color:rgb(195, 232, 141)">"S"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Expect</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"a bounce on the release"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">         </span><span class="token string" style="color:rgb(195, 232, 141)">"001111111111111111111101001000000000"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">    </span><span class="token string" style="color:rgb(195, 232, 141)">"S"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">memset</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">longHold</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token char" style="color:rgb(130, 170, 255)">'1'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">150u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> longHold</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token number" style="color:rgb(247, 140, 108)">150</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token char" style="color:rgb(130, 170, 255)">'\0'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">       </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* 1.5 s */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Expect</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"a 1.5 s hold"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">                    longHold</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">                                  </span><span class="token string" style="color:rgb(195, 232, 141)">"L"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">memset</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">longHold</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token char" style="color:rgb(130, 170, 255)">'1'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">99u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">  longHold</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token number" style="color:rgb(247, 140, 108)">99</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token char" style="color:rgb(130, 170, 255)">'\0'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">        </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* just under the limit (+ debounce) */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Expect</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"a 0.99 s hold"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">                   longHold</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">                                  </span><span class="token string" style="color:rgb(195, 232, 141)">"S"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">puts</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"all state machine tests passed"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Вивід запуску — справжній:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">nothing happens                    -&gt; (no event)</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">a 20 ms glitch                     -&gt; (no event)</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">a clean 200 ms press               -&gt; S</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">a press with a bouncing contact    -&gt; S</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">a bounce on the release            -&gt; S</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">a 1.5 s hold                       -&gt; L</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">a 0.99 s hold                      -&gt; S</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">all state machine tests passed</span><br></div></code></pre></div></div>
<p>Два останні рядки — це випадок, який прапорці завжди обробляють неправильно. Утримання 1,5 секунди дає <strong>одне</strong> довге натискання і жодного короткого після відпускання, а утримання трохи менше за межу — це коротке натискання. Обидва результати випливають із таблиці, а не з ретельного програмування.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="що-ви-отримуєте-і-чого-це-коштує">Що ви отримуєте і чого це коштує<a href="https://embedbits.cz/uk/blog/fsm-in-practice-button#%D1%89%D0%BE-%D0%B2%D0%B8-%D0%BE%D1%82%D1%80%D0%B8%D0%BC%D1%83%D1%94%D1%82%D0%B5-%D1%96-%D1%87%D0%BE%D0%B3%D0%BE-%D1%86%D0%B5-%D0%BA%D0%BE%D1%88%D1%82%D1%83%D1%94" class="hash-link" aria-label="Пряме посилання на Що ви отримуєте і чого це коштує" title="Пряме посилання на Що ви отримуєте і чого це коштує" translate="no">​</a></h2>
<p><strong>Ви отримуєте:</strong></p>
<ul>
<li class=""><strong>читабельну специфікацію</strong>: таблиця вище і код — це одне й те саме,</li>
<li class=""><strong>гарантію «рівно один раз»</strong>: <code>Entry</code> і <code>Leave</code> виконуються по одному разу на перехід, події не дублюються,</li>
<li class=""><strong>місце для всього</strong>: нова вимога (подвійний клік) — це новий стан або новий рядок у таблиці, а не правка трьох вкладених умов,</li>
<li class=""><strong>автомат, який можна тестувати на ПК</strong> з рядком як входом, як ви щойно бачили.</li>
</ul>
<p><strong>Це коштує:</strong></p>
<ul>
<li class=""><strong>шаблонний код (boilerplate)</strong>: чотири функції на стан, більшість із них порожні. Це плата за однорідність, і вона окупається з 5-м станом. Для автомата з двома станами краще <code>switch</code>,</li>
<li class=""><strong>таблицю переходів треба продумати</strong> до написання коду. Це не витрата, але на початку так здається.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="підводні-камені">Підводні камені<a href="https://embedbits.cz/uk/blog/fsm-in-practice-button#%D0%BF%D1%96%D0%B4%D0%B2%D0%BE%D0%B4%D0%BD%D1%96-%D0%BA%D0%B0%D0%BC%D0%B5%D0%BD%D1%96" class="hash-link" aria-label="Пряме посилання на Підводні камені" title="Пряме посилання на Підводні камені" translate="no">​</a></h2>
<ol>
<li class=""><strong>Час належить періоду задачі, а не затримці.</strong> Автомат рахує запуски, тож межі (50 мс, 1 с) — це кількість тиків. Ніколи не викликайте затримку в стані, вона блокує всі інші автомати.</li>
<li class=""><strong>Семплуйте в задачі, а не в перериванні.</strong> Переривання піна бачило б кожне деренчання. Задача, що запускається кожні 10 мс, — це безкоштовний фільтр низьких частот.</li>
<li class=""><strong>Єдиний слот для події губить події,</strong> якщо застосунок читає її повільніше, ніж автомат створює. Для кнопки це не має значення, для протоколу має: використовуйте чергу.</li>
<li class=""><strong>Недійсний стан — це теж стан.</strong> Перевірка діапазону в ядрі відправляє пошкоджену змінну в безпечний стан замість читання таблиці за її межами (тут діють правила MISRA про <a class="" href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice">undefined behavior</a>).</li>
<li class=""><strong>Автомат із 30 станами — ознака відсутньої ієрархії.</strong> Розділіть його на кілька автоматів, де один є <em>станом</em> іншого, і тримайте кожен достатньо малим, щоб він умістився в таблицю на одному екрані.</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="виправлення-в-шаблоні">Виправлення в шаблоні<a href="https://embedbits.cz/uk/blog/fsm-in-practice-button#%D0%B2%D0%B8%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%BD%D1%8F-%D0%B2-%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD%D1%96" class="hash-link" aria-label="Пряме посилання на Виправлення в шаблоні" title="Пряме посилання на Виправлення в шаблоні" translate="no">​</a></h2>
<p>Коли я порівняв приклад із шаблоном із першої статті, я знайшов у шаблоні дві помилки й виправив їх там (пізніша перевірка шаблону знайшла ще, див. <a class="" href="https://embedbits.cz/uk/blog/finite-state-machines#what-was-fixed">перелік виправлень</a>): початкове значення змінних стану було залишком назви з іншого проєкту (<code>APPCORE_HANDLER_STATE_1</code> замість <code>&lt;MODULE&gt;_STATE_1</code>), а в останньому рядку таблиці стояло <code>State_2_Leave</code> замість <code>State_3_Leave</code>. Друга — це той тип помилки, якому присвячена ця стаття: код компілюється, а автомат викликає неправильну підпрограму при виході з третього стану. Таблиця з designated initializers трохи ускладнює таку помилку, а тест, що проходить усі стани, робить її неможливою.</p>]]></content:encoded>
            <category>Вбудовані системи</category>
            <category>C</category>
            <category>Архітектура</category>
        </item>
        <item>
            <title><![CDATA[Один BSP, багато родин STM32: чому гілки Git і чого вони коштують]]></title>
            <link>https://embedbits.cz/uk/blog/bsp-stm32-families-git-branches</link>
            <guid>https://embedbits.cz/uk/blog/bsp-stm32-families-git-branches</guid>
            <pubDate>Mon, 05 Oct 2026 18:00:00 GMT</pubDate>
            <description><![CDATA[STM32 - це не один мікроконтролер, а десяток родин: G4, H5, U5, F4 і так далі. Вони мають те саме ядро Cortex-M, але різну периферію, різні назви регістрів і різний пакет драйверів від виробника. Якщо ви хочете, щоб одна архітектура firmware працювала на всіх них, потрібно вирішити, де житимуть відмінності. Безкоштовного варіанту немає, і ця стаття описує той, який я обрав для Embedbits BSP, що про нього кажуть цифри зі справжніх репозиторіїв і чого він коштує.]]></description>
            <content:encoded><![CDATA[<p>STM32 - це не один мікроконтролер, а десяток родин: G4, H5, U5, F4 і так далі. Вони мають те саме ядро Cortex-M, але різну периферію, різні назви регістрів і різний пакет драйверів від виробника. Якщо ви хочете, щоб одна архітектура firmware працювала на всіх них, потрібно вирішити, де житимуть відмінності. Безкоштовного варіанту немає, і ця стаття описує той, який я обрав для Embedbits BSP, що про нього кажуть цифри зі справжніх репозиторіїв і чого він коштує.</p>
<!-- -->
<p>Усі наведені нижче цифри взято з репозиторіїв на 5 жовтня 2026 року, виміряні невеликим скриптом, який ви знайдете наприкінці розділу про вартість. Кілька слів про дату: на той момент <strong>STM32F4 - єдина родина з повним релізом</strong> BSP, а реліз інших (першою U5) саме робився. Тож цифри показують платформу посеред міграції, а не кінцевий стан, і саме це робить їх цікавими. Запустіть скрипт ще раз після релізу й порівняйте.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="чотири-способи-підтримати-багато-родин">Чотири способи підтримати багато родин<a href="https://embedbits.cz/uk/blog/bsp-stm32-families-git-branches#%D1%87%D0%BE%D1%82%D0%B8%D1%80%D0%B8-%D1%81%D0%BF%D0%BE%D1%81%D0%BE%D0%B1%D0%B8-%D0%BF%D1%96%D0%B4%D1%82%D1%80%D0%B8%D0%BC%D0%B0%D1%82%D0%B8-%D0%B1%D0%B0%D0%B3%D0%B0%D1%82%D0%BE-%D1%80%D0%BE%D0%B4%D0%B8%D0%BD" class="hash-link" aria-label="Пряме посилання на Чотири способи підтримати багато родин" title="Пряме посилання на Чотири способи підтримати багато родин" translate="no">​</a></h2>






























<table><thead><tr><th>Підхід</th><th>Як це виглядає</th><th>Проблема</th></tr></thead><tbody><tr><td><strong>Одне дерево, <code>#ifdef</code></strong></td><td><code>#if defined(STM32G4) ... #elif defined(STM32U5) ...</code> у коді</td><td>Код наповнюється умовами, більша його частина мертва для будь-якої конкретної збірки (див. правила щодо мертвого коду в статті про <a class="" href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice">MISRA</a>), і ніхто не наважується його чіпати</td></tr><tr><td><strong>Copy and paste</strong></td><td>Тека на родину, копія всього</td><td>Кожне виправлення треба робити N разів, вручну, і одне з них забувається</td></tr><tr><td><strong>Один репозиторій на родину</strong></td><td><code>Bsp-G4</code>, <code>Bsp-U5</code>, ...</td><td>Те саме, що й копіювання, тільки з більшою кількістю репозиторіїв</td></tr><tr><td><strong>Абстракція + гілка на родину</strong></td><td>Один інтерфейс, родина живе в гілці</td><td>Гілка може віддалитися від інших (див. нижче)</td></tr></tbody></table>
<p>Я використовую останній. Ідея в тому, що користувач обирає родину <strong>один раз</strong>, і відтоді дерево містить лише код цієї родини.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="як-це-працює-в-bsp">Як це працює в BSP<a href="https://embedbits.cz/uk/blog/bsp-stm32-families-git-branches#%D1%8F%D0%BA-%D1%86%D0%B5-%D0%BF%D1%80%D0%B0%D1%86%D1%8E%D1%94-%D0%B2-bsp" class="hash-link" aria-label="Пряме посилання на Як це працює в BSP" title="Пряме посилання на Як це працює в BSP" translate="no">​</a></h2>
<p>Кожна родина STM32 - це <strong>гілка Git</strong> репозиторію BSP (<code>STM32G4</code>, <code>STM32H5</code>, <code>STM32U5</code>, ...). Те саме стосується модулів BSP: модулів MCAL, linker і RAL. Решту робить платформа. Коли ви запускаєте скрипт налаштування EmBi_Platform і обираєте <em>Configure BSP module</em>, він показує список гілок:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">Available BSP Families:</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">[0]: STM32G4</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">[1]: STM32G4_Dev</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">[2]: STM32H5</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">[3]: STM32H5_Dev</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">[4]: STM32U5</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">[5]: master</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">Enter branch ID (numerical):</span><br></div></code></pre></div></div>
<p>а після вашого вибору додає модулі BSP як підмодулі Git, робить checkout гілки та комітів родини й оновлює файли CMake. Проєкт прив'язаний до точних комітів, тож завтра збирається так само. Записи з <code>_Dev</code> - це гілки розробки родини, а <code>master</code> містить найновіші нерелізні зміни всіх родин, тому призначений для роботи над платформою, а не для продукту. Перемикання на іншу родину означає повторний запуск тієї самої опції.</p>
<p>Пакет драйверів виробника йде за тією самою схемою. RAL має гілки <code>STM32&lt;family&gt;</code> для родини, <code>STM32&lt;family&gt;_&lt;major&gt;.x</code> для мажорного релізу драйверів ST і <code>STM32&lt;family&gt;_&lt;major&gt;.&lt;minor&gt;</code> для мінорного. Коли ST публікує нову версію, з'являється нова гілка, а стара лишається доступною для проєктів, які її використовують.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="де-насправді-відмінності">Де насправді відмінності<a href="https://embedbits.cz/uk/blog/bsp-stm32-families-git-branches#%D0%B4%D0%B5-%D0%BD%D0%B0%D1%81%D0%BF%D1%80%D0%B0%D0%B2%D0%B4%D1%96-%D0%B2%D1%96%D0%B4%D0%BC%D1%96%D0%BD%D0%BD%D0%BE%D1%81%D1%82%D1%96" class="hash-link" aria-label="Пряме посилання на Де насправді відмінності" title="Пряме посилання на Де насправді відмінності" translate="no">​</a></h2>
<p>Я хотів знати, яка частина коду справді відрізняється між родинами, тож порівняв гілки. Ось модуль GPIO, <code>Bsp-Mcal-Gpio</code>, з релізною STM32F4 як еталоном. Числа - це змінені рядки (додані плюс видалені) у кожному файлі:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">file               STM32F4 vs STM32G4   STM32F4 vs STM32H5   STM32F4 vs STM32U5</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">Gpio.c             306                  142                  303</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">Gpio.h             5                    0                    0</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">Gpio_Port.h        3                    0                    0</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">Gpio_Types.h       35                   5                    28</span><br></div></code></pre></div></div>
<p>Публічний інтерфейс, <code>Gpio_Port.h</code>, <strong>ідентичний</strong> на F4 і H5, а на G4 та U5 відрізняється трьома рядками коментаря. G4 і U5 близькі одна до одної (їхні <code>Gpio.c</code> відрізняються трьома рядками, знову лише коментар): це старіше покоління модуля, яке чекає на реліз. Чому <em>код модуля</em> такий схожий у двох різних родинах? Тому що відмінність винесено з модуля на найнижчий рівень. RAL має теку <code>Port</code> з одним заголовком на кожну периферію, і вся різниця між родинами в <code>Stm32_gpio.h</code> - це ось що:</p>
<div class="language-diff codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-diff codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token deleted-sign deleted prefix deleted" style="color:rgb(255, 85, 114)">-</span><span class="token deleted-sign deleted line" style="color:rgb(255, 85, 114)">#include "stm32g4xx_ll_gpio.h"              /* GPIO peripheral access layer   */</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token deleted-sign deleted line" style="color:rgb(255, 85, 114)"></span><span class="token inserted-sign inserted prefix inserted" style="color:rgb(195, 232, 141)">+</span><span class="token inserted-sign inserted line" style="color:rgb(195, 232, 141)">#include "stm32u5xx_ll_gpio.h"              /* GPIO peripheral access layer   */</span><br></div></code></pre></div></div>
<p>Один рядок. Модуль MCAL включає <code>Stm32_gpio.h</code> і ніколи не знає, який драйвер виробника за ним стоїть. Те саме стосується інших периферій, які я порівняв між G4 та U5 (<code>Stm32_rcc.h</code>, <code>Stm32_usart.h</code>, <code>Stm32_tim.h</code>): два відмінні рядки з 32. У цьому вся ідея шарової архітектури, застосована до питання родин.</p>
<p>Що справді відрізняється, так це <strong>набори периферії</strong>. Тека <code>Port</code> для G4 має заголовки для HRTIM і DMAMUX, яких немає в U5, а U5 має кеші, PKA, SDMMC і low-power GPIO, яких немає в G4. Модуль для периферії, якої родина не має, просто відсутній у її гілці, і звідси береться таблиця <em>підтримки родин</em> на сторінці про <a class="" href="https://embedbits.cz/uk/docs/bsp/mcal">MCAL</a>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="два-рівні-родина-і-mcu">Два рівні: родина і MCU<a href="https://embedbits.cz/uk/blog/bsp-stm32-families-git-branches#%D0%B4%D0%B2%D0%B0-%D1%80%D1%96%D0%B2%D0%BD%D1%96-%D1%80%D0%BE%D0%B4%D0%B8%D0%BD%D0%B0-%D1%96-mcu" class="hash-link" aria-label="Пряме посилання на Два рівні: родина і MCU" title="Пряме посилання на Два рівні: родина і MCU" translate="no">​</a></h3>
<p>Гілка вирішує відмінність між <em>родинами</em>. Існує менша відмінність <em>всередині</em> родини: окремі MCU мають різну кількість портів (MCU в малому корпусі має менше портів GPIO, ніж у великому). Її розв'язують умовою на макроси, які заголовок пристрою від виробника вже визначає, а не гілкою:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PortConfig_t</span><span class="token plain">      gpio_PeriphConf</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain"> GPIO_PORT_CNT </span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">if</span><span class="token macro property"> </span><span class="token macro property expression function" style="color:rgb(130, 170, 255)">defined</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression">GPIOA</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">GpioReg </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> GPIOA</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">GpioRcc </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> RCC_PERIPH_GPIOA </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ... */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">if</span><span class="token macro property"> </span><span class="token macro property expression function" style="color:rgb(130, 170, 255)">defined</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression">GPIOJ</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">GpioReg </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> GPIOJ</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">GpioRcc </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> RCC_PERIPH_GPIOJ </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">if</span><span class="token macro property"> </span><span class="token macro property expression function" style="color:rgb(130, 170, 255)">defined</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression">GPIOK</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">GpioReg </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> GPIOK</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">GpioRcc </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> RCC_PERIPH_GPIOK </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>а в типах порт, якого не існує, відображається на значення <code>GPIO_PORT_CNT</code>, тож інші модулі й далі компілюються, а використання такого порту можна розпізнати як некоректне. Правило: <strong>гілка для іншої родини, <code>#if defined</code> макросу виробника для іншого MCU в тій самій родині.</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="що-ви-отримуєте">Що ви отримуєте<a href="https://embedbits.cz/uk/blog/bsp-stm32-families-git-branches#%D1%89%D0%BE-%D0%B2%D0%B8-%D0%BE%D1%82%D1%80%D0%B8%D0%BC%D1%83%D1%94%D1%82%D0%B5" class="hash-link" aria-label="Пряме посилання на Що ви отримуєте" title="Пряме посилання на Що ви отримуєте" translate="no">​</a></h2>
<ul>
<li class=""><strong>Лише код родини.</strong> Жодних лісів <code>#ifdef</code>, нічого мертвого, а diff зміни показує тільки той код, який збирається.</li>
<li class=""><strong>Чітке відокремлення коду виробника.</strong> Драйвери ST для кожної родини віддзеркалені у власних гілках RAL і не модифікуються. Ваш код їх не містить.</li>
<li class=""><strong>Стабільний інтерфейс для застосунку.</strong> Застосунок викликає <code>Gpio_Set_PinLevel()</code> і не знає родини, тож зміна MCU - це інша гілка, а не переписування застосунку.</li>
<li class=""><strong>Зафіксовані версії.</strong> Проєкт точно каже, який коміт якої гілки він використовує, тож оновлення - це свідоме рішення.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="чого-це-коштує-дрейф">Чого це коштує: дрейф<a href="https://embedbits.cz/uk/blog/bsp-stm32-families-git-branches#%D1%87%D0%BE%D0%B3%D0%BE-%D1%86%D0%B5-%D0%BA%D0%BE%D1%88%D1%82%D1%83%D1%94-%D0%B4%D1%80%D0%B5%D0%B9%D1%84" class="hash-link" aria-label="Пряме посилання на Чого це коштує: дрейф" title="Пряме посилання на Чого це коштує: дрейф" translate="no">​</a></h2>
<p>Слабкість гілок у тому, що вони живуть власним життям. Виправлення, зроблене в одній гілці, не потрапляє в інші, якщо хтось його туди не перенесе. Вимірювання вище показує два <em>покоління</em> того самого модуля одночасно. Релізна F4 (і H5, яка йде за нею) має вдосконалену ініціалізацію GPIO (рівень виходу встановлюється <em>до</em> перемикання режиму піна на вихід, тож пін не дає глітчу), задокументовані параметри та теку <code>Tests</code> з <strong>модульними й інтеграційними тестами</strong>. G4 і U5 нічого з цього ще не мають. Публічний інтерфейс USART у <code>Usart_Port.h</code> показує це ще краще:</p>






























<table><thead><tr><th>Гілка</th><th>Публічні функції</th><th>Порівняно з F4</th></tr></thead><tbody><tr><td>STM32F4 (реліз)</td><td>79</td><td>еталон</td></tr><tr><td>STM32H5</td><td>79</td><td>той самий набір функцій, заголовок відрізняється у 2 рядках</td></tr><tr><td>STM32G4</td><td>82</td><td>12 функцій, яких немає в F4 (<code>Usart_StartTransmit</code>, <code>Usart_Set_TransmitBytes</code>, ...) і 9, які є в F4, а в G4 немає (<code>Usart_Set_TxStart</code>, <code>Usart_Set_DataConfig</code>, ...)</td></tr><tr><td>STM32U5</td><td>83</td><td>13 функцій лише в U5, 9 лише в F4</td></tr></tbody></table>
<p>Наразі код, написаний під інтерфейс F4, компілюється на H5 і не компілюється на G4 та U5. Це не вада підходу, а нормальний стан платформи посеред релізу. Але він показує ціну: обіцянка «той самий інтерфейс BSP для всіх родин» вірна для родини лише <strong>після того, як її гілку випущено</strong>, а час між релізом першої та останньої родини - це період, коли гілки віддаляються одна від одної. У цей час ніхто не повинен писати застосунок для родини, яка ще не готова.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="як-тримати-дрейф-під-контролем">Як тримати дрейф під контролем<a href="https://embedbits.cz/uk/blog/bsp-stm32-families-git-branches#%D1%8F%D0%BA-%D1%82%D1%80%D0%B8%D0%BC%D0%B0%D1%82%D0%B8-%D0%B4%D1%80%D0%B5%D0%B9%D1%84-%D0%BF%D1%96%D0%B4-%D0%BA%D0%BE%D0%BD%D1%82%D1%80%D0%BE%D0%BB%D0%B5%D0%BC" class="hash-link" aria-label="Пряме посилання на Як тримати дрейф під контролем" title="Пряме посилання на Як тримати дрейф під контролем" translate="no">​</a></h3>
<ol>
<li class=""><strong>Майте еталонну гілку та список того, що портовано.</strong> Одна родина веде (тут F4), інші наздоганяють, а статус видимий: це таблиця підтримки родин на сторінці MCAL, розширена колонкою «та сама версія інтерфейсу, що й в еталона».</li>
<li class=""><strong>Зробіть реліз однією операцією.</strong> Скрипт релізу, що проходить усі модулі однієї родини (так робиться реліз U5), кращий за ручний merge: родина або готова вся, або не готова.</li>
<li class=""><strong>Вимірюйте дрейф.</strong> Скрипт нижче порівнює один модуль на кількох гілках і виводить кількість змінених рядків на файл. Нуль або коментар означає «те саме», велике число - це робота, що чекає. Він виконується за секунду і вміщається в job CI.</li>
<li class=""><strong>Нехай один набір тестів перевіряє всі гілки.</strong> Модульні тести, що існують на F4 (і H5), - це специфікація модуля. Якщо той самий <code>Test_Gpio.c</code> має проходити на кожній гілці родини, інтерфейс не може непомітно розійтися. Стаття про <a class="" href="https://embedbits.cz/uk/blog/unit-testing-unity-cmock">модульне тестування</a> показує, як будується такий тест.</li>
<li class=""><strong>Тримайте відмінність внизу.</strong> Усе, що можна перенести в заголовки <code>Port</code> у RAL (один <code>#include</code>, як показано вище), - це відмінність, яку не треба підтримувати на кожній гілці. Чим більше коду <em>ідентичного</em>, тим легше перенести виправлення з однієї гілки в іншу (<code>git cherry-pick</code>).</li>
</ol>
<div class="language-sh codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">branch-drift.sh</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sh codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token shebang important">#!/usr/bin/env bash</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># Shows how much a module differs between its family branches.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># Usage: branch-drift.sh &lt;repository-url&gt; &lt;branch&gt; &lt;branch&gt; [&lt;branch&gt;...]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">set</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-euo</span><span class="token plain"> pipefail</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(191, 199, 213)">repo</span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$1</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">shift</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(191, 199, 213)">branches</span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$@</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(191, 199, 213)">work</span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$(</span><span class="token string variable" style="color:rgb(191, 199, 213)">mktemp </span><span class="token string variable parameter variable" style="color:rgb(191, 199, 213)">-d</span><span class="token string variable" style="color:rgb(191, 199, 213)">)</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">git</span><span class="token plain"> init </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-q</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$work</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">git</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-C</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$work</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"> remote </span><span class="token function" style="color:rgb(130, 170, 255)">add</span><span class="token plain"> origin </span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$repo</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">for</span><span class="token plain"> </span><span class="token for-or-select variable" style="color:rgb(191, 199, 213)">branch</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">in</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">${branches</span><span class="token string variable punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token string variable" style="color:rgb(191, 199, 213)">@</span><span class="token string variable punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token string variable" style="color:rgb(191, 199, 213)">}</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">do</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">git</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-C</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$work</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"> fetch </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-q</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">--depth</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token plain"> origin </span><span class="token string" style="color:rgb(195, 232, 141)">"refs/heads/</span><span class="token string variable" style="color:rgb(191, 199, 213)">$branch</span><span class="token string" style="color:rgb(195, 232, 141)">:refs/remotes/origin/</span><span class="token string variable" style="color:rgb(191, 199, 213)">$branch</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">done</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(191, 199, 213)">files</span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$(</span><span class="token string variable function" style="color:rgb(130, 170, 255)">git</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable parameter variable" style="color:rgb(191, 199, 213)">-C</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable string variable" style="color:rgb(191, 199, 213)">$work</span><span class="token string variable string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)"> ls-tree </span><span class="token string variable parameter variable" style="color:rgb(191, 199, 213)">-r</span><span class="token string variable" style="color:rgb(191, 199, 213)"> --name-only </span><span class="token string variable string" style="color:rgb(195, 232, 141)">"origin/</span><span class="token string variable string variable" style="color:rgb(191, 199, 213)">${branches</span><span class="token string variable string variable punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token string variable string variable" style="color:rgb(191, 199, 213)">0</span><span class="token string variable string variable punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token string variable string variable" style="color:rgb(191, 199, 213)">}</span><span class="token string variable string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable operator" style="color:rgb(137, 221, 255)">|</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable function" style="color:rgb(130, 170, 255)">grep</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable parameter variable" style="color:rgb(191, 199, 213)">-E</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable string" style="color:rgb(195, 232, 141)">'\.(c|h)$'</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable operator" style="color:rgb(137, 221, 255)">||</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable boolean" style="color:rgb(255, 88, 116)">true</span><span class="token string variable" style="color:rgb(191, 199, 213)">)</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">printf</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'%-44s'</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"file"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">for</span><span class="token plain"> </span><span class="token variable punctuation" style="color:rgb(199, 146, 234)">((</span><span class="token variable" style="color:rgb(191, 199, 213)">i </span><span class="token variable operator" style="color:rgb(137, 221, 255)">=</span><span class="token variable" style="color:rgb(191, 199, 213)"> </span><span class="token variable number" style="color:rgb(247, 140, 108)">1</span><span class="token variable punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token variable" style="color:rgb(191, 199, 213)"> i </span><span class="token variable operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token variable" style="color:rgb(191, 199, 213)"> ${#branches[@]}</span><span class="token variable punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token variable" style="color:rgb(191, 199, 213)"> i</span><span class="token variable operator" style="color:rgb(137, 221, 255)">++</span><span class="token variable punctuation" style="color:rgb(199, 146, 234)">))</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">do</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">printf</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'%-24s'</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">${branches</span><span class="token string variable punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token string variable" style="color:rgb(191, 199, 213)">0</span><span class="token string variable punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token string variable" style="color:rgb(191, 199, 213)">}</span><span class="token string" style="color:rgb(195, 232, 141)"> vs </span><span class="token string variable" style="color:rgb(191, 199, 213)">${branches</span><span class="token string variable punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token string variable" style="color:rgb(191, 199, 213)">i</span><span class="token string variable punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token string variable" style="color:rgb(191, 199, 213)">}</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">done</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">printf</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'\n'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">for</span><span class="token plain"> </span><span class="token for-or-select variable" style="color:rgb(191, 199, 213)">file</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">in</span><span class="token plain"> </span><span class="token variable" style="color:rgb(191, 199, 213)">$files</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">do</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">printf</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'%-44s'</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$file</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">for</span><span class="token plain"> </span><span class="token variable punctuation" style="color:rgb(199, 146, 234)">((</span><span class="token variable" style="color:rgb(191, 199, 213)">i </span><span class="token variable operator" style="color:rgb(137, 221, 255)">=</span><span class="token variable" style="color:rgb(191, 199, 213)"> </span><span class="token variable number" style="color:rgb(247, 140, 108)">1</span><span class="token variable punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token variable" style="color:rgb(191, 199, 213)"> i </span><span class="token variable operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token variable" style="color:rgb(191, 199, 213)"> ${#branches[@]}</span><span class="token variable punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token variable" style="color:rgb(191, 199, 213)"> i</span><span class="token variable operator" style="color:rgb(137, 221, 255)">++</span><span class="token variable punctuation" style="color:rgb(199, 146, 234)">))</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">do</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token assign-left variable" style="color:rgb(191, 199, 213)">changed</span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$(</span><span class="token string variable function" style="color:rgb(130, 170, 255)">git</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable parameter variable" style="color:rgb(191, 199, 213)">-C</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable string variable" style="color:rgb(191, 199, 213)">$work</span><span class="token string variable string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable function" style="color:rgb(130, 170, 255)">diff</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable parameter variable" style="color:rgb(191, 199, 213)">--numstat</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable string" style="color:rgb(195, 232, 141)">"origin/</span><span class="token string variable string variable" style="color:rgb(191, 199, 213)">${branches</span><span class="token string variable string variable punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token string variable string variable" style="color:rgb(191, 199, 213)">0</span><span class="token string variable string variable punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token string variable string variable" style="color:rgb(191, 199, 213)">}</span><span class="token string variable string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable string" style="color:rgb(195, 232, 141)">"origin/</span><span class="token string variable string variable" style="color:rgb(191, 199, 213)">${branches</span><span class="token string variable string variable punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token string variable string variable" style="color:rgb(191, 199, 213)">i</span><span class="token string variable string variable punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token string variable string variable" style="color:rgb(191, 199, 213)">}</span><span class="token string variable string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)"> -- </span><span class="token string variable string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable string variable" style="color:rgb(191, 199, 213)">$file</span><span class="token string variable string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable operator" style="color:rgb(137, 221, 255)">|</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable function" style="color:rgb(130, 170, 255)">awk</span><span class="token string variable" style="color:rgb(191, 199, 213)"> </span><span class="token string variable string" style="color:rgb(195, 232, 141)">'{print $1 + $2}'</span><span class="token string variable" style="color:rgb(191, 199, 213)">)</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">printf</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'%-24s'</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">${changed</span><span class="token string variable operator" style="color:rgb(137, 221, 255)">:-</span><span class="token string variable" style="color:rgb(191, 199, 213)">0}</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">done</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">printf</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'\n'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">done</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">rm</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-rf</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"</span><span class="token string variable" style="color:rgb(191, 199, 213)">$work</span><span class="token string" style="color:rgb(195, 232, 141)">"</span><br></div></code></pre></div></div>
<p>Використання на гілках модуля GPIO (таблиця вище - його вивід):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">./branch-drift.sh https://github.com/Embedbits/Bsp-Mcal-Gpio STM32F4 STM32G4 STM32H5 STM32U5</span><br></div></code></pre></div></div>
<p>Для інших модулів картина інша, і це корисно знати до того, як планувати роботу:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">file             F4 vs G4   F4 vs H5   F4 vs U5</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">Exti_Port.h      0          0          0</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">Exti.c           586        538        682</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">Usart_Port.h     55         2          28</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">Usart.c          3734       2301       3927</span><br></div></code></pre></div></div>
<p>EXTI має незмінний публічний заголовок на всіх гілках і різну реалізацію (периферія відрізняється, інтерфейс ні). USART відрізняється інтерфейсом на G4 та U5 і значною частиною реалізації, що є мірою того, скільки роботи потребує реліз цих двох.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="який-підхід-обрати">Який підхід обрати<a href="https://embedbits.cz/uk/blog/bsp-stm32-families-git-branches#%D1%8F%D0%BA%D0%B8%D0%B9-%D0%BF%D1%96%D0%B4%D1%85%D1%96%D0%B4-%D0%BE%D0%B1%D1%80%D0%B0%D1%82%D0%B8" class="hash-link" aria-label="Пряме посилання на Який підхід обрати" title="Пряме посилання на Який підхід обрати" translate="no">​</a></h2>
<ul>
<li class=""><strong>Гілки на родину</strong> підходять, коли родини відрізняються <em>набором</em> периферії та драйверами виробника, коли продукт збирається для кількох родин і коли ви можете дозволити собі підтримувати інтерфейс (дрейфом можна керувати при кількох родинах і з інструментом).</li>
<li class=""><strong><code>#ifdef</code> в одному дереві</strong> прийнятний, коли відмінності малі: одна родина з кількома варіантами MCU, один регістр, що називається по-іншому.</li>
<li class=""><strong>Жоден із них</strong> не допоможе без <strong>абстракції внизу</strong>. Гілка лише вирішує, яка реалізація інтерфейсу лежить у дереві. Якщо застосунок включає заголовки виробника, гілка на родину ні від чого вас не врятує.</li>
</ul>
<p>І що б ви не обрали, запишіть, яка родина еталонна, і тримайте в job CI одне число: наскільки від неї відстають інші.</p>]]></content:encoded>
            <category>Вбудовані системи</category>
            <category>Архітектура</category>
            <category>STM32</category>
        </item>
        <item>
            <title><![CDATA[Що відбувається перед main(): скидання, startup-код і linker script]]></title>
            <link>https://embedbits.cz/uk/blog/before-main-startup-linker</link>
            <guid>https://embedbits.cz/uk/blog/before-main-startup-linker</guid>
            <pubDate>Mon, 05 Oct 2026 14:00:00 GMT</pubDate>
            <description><![CDATA[Кожен підручник із C починається з int main(void). Ніхто не пояснює, хто його викликає. А втім, коли ви пишете uint32_t counter = 5; як глобальну змінну і перший рядок main() зчитує 5, вже була виконана велика робота. Коли ця робота не виконана, симптом — змінна з випадковим значенням, яка «вчора працювала».]]></description>
            <content:encoded><![CDATA[<p>Кожен підручник із C починається з <code>int main(void)</code>. Ніхто не пояснює, хто його викликає. А втім, коли ви пишете <code>uint32_t counter = 5;</code> як глобальну змінну і перший рядок <code>main()</code> зчитує 5, вже була виконана велика робота. Коли ця робота не виконана, симптом — змінна з випадковим значенням, яка «вчора працювала».</p>
<p>У цій статті ми простежимо шлях мікроконтролера від скидання до першого рядка <code>main()</code>: що апаратна частина робить сама, що каже linker script і що має зробити startup-код. Це вміст двох модулів BSP Embedbits (<a class="" href="https://embedbits.cz/uk/docs/bsp/linker">Linker</a> і <a class="" href="https://embedbits.cz/uk/docs/bsp/startup">Startup</a>), але принцип той самий на кожному Cortex-M. Увесь код у статті зібрано за допомогою <code>arm-none-eabi-gcc</code> 13.2.1 і <strong>запущено</strong> в QEMU на моделі плати STM32F405, тож адреси та виводи справжні.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="чого-очікує-мова-c">Чого очікує мова C<a href="https://embedbits.cz/uk/blog/before-main-startup-linker#%D1%87%D0%BE%D0%B3%D0%BE-%D0%BE%D1%87%D1%96%D0%BA%D1%83%D1%94-%D0%BC%D0%BE%D0%B2%D0%B0-c" class="hash-link" aria-label="Пряме посилання на Чого очікує мова C" title="Пряме посилання на Чого очікує мова C" translate="no">​</a></h2>
<p>Стандарт C обіцяє про програму кілька речей, про які апаратна частина не знає:</p>
<ul>
<li class="">глобальна змінна з ініціалізатором (<code>uint32_t initializedValue = 0x12345678u;</code>) має це значення на старті,</li>
<li class="">глобальна або <code>static</code> змінна без ініціалізатора дорівнює <strong>нулю</strong>,</li>
<li class="">існує стек, і функція може ним користуватися,</li>
<li class="">глобальні об'єкти C++ конструюються (а в C виконуються функції, позначені <code>__attribute__((constructor))</code>) до <code>main()</code>.</li>
</ul>
<p>Апаратна частина дає дуже мало. Після скидання ядро Cortex-M робить лише дві речі: зчитує <strong>початкове значення вказівника стека</strong> з першого слова таблиці векторів і <strong>адресу обробника скидання</strong> з другого та переходить туди. Усе інше — робота startup-коду, який є частиною <strong>вашої</strong> прошивки.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="таблиця-векторів">Таблиця векторів<a href="https://embedbits.cz/uk/blog/before-main-startup-linker#%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D1%8F-%D0%B2%D0%B5%D0%BA%D1%82%D0%BE%D1%80%D1%96%D0%B2" class="hash-link" aria-label="Пряме посилання на Таблиця векторів" title="Пряме посилання на Таблиця векторів" translate="no">​</a></h2>
<p>Таблиця векторів — це масив адрес на початку flash (у STM32 це <code>0x08000000</code>, що також видно за адресою 0 після завантаження з flash). У мінімальному вигляді вона містить вказівник стека, обробник скидання та обробники винятків:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">__attribute__</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token function" style="color:rgb(130, 170, 255)">section</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">".isr_vector"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> used</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> vectorTable</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">_estack</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* [0] initial stack pointer */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    Reset_Handler</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">              </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* [1] where the core jumps after the reset */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    NMI_Handler</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    HardFault_Handler</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>Ось що містять перші 16 байтів зібраної прошивки:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">Contents of section .isr_vector:</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"> 8000000 00000220 13000008 11000008 11000008</span><br></div></code></pre></div></div>
<p>Числа записані в порядку little-endian. Перше слово — <code>0x20020000</code>, кінець 128 кБ RAM, що починається з <code>0x20000000</code>, тобто місце, де починається стек (він росте вниз). Друге — <code>0x08000013</code>: обробник скидання розташований за адресою <code>0x08000012</code>, а наймолодший біт означає «код Thumb», що обов'язково на Cortex-M. Решта — винятки; усі невикористані я спрямував на один і той самий обробник.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="linker-script-карта-памяті">Linker script: карта пам'яті<a href="https://embedbits.cz/uk/blog/before-main-startup-linker#linker-script-%D0%BA%D0%B0%D1%80%D1%82%D0%B0-%D0%BF%D0%B0%D0%BC%D1%8F%D1%82%D1%96" class="hash-link" aria-label="Пряме посилання на Linker script: карта пам'яті" title="Пряме посилання на Linker script: карта пам'яті" translate="no">​</a></h2>
<p>Компілятор створює об'єктні файли із секціями (<code>.text</code> для коду, <code>.data</code>, <code>.bss</code> тощо) і не має уявлення, де в пам'яті вони опиняться. Це вирішує лінкер, і йому потрібна карта. Ось увесь скрипт прикладу, 59 рядків:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">link.ld</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">/* Entry point: the first instruction that runs after the reset */</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">ENTRY(Reset_Handler)</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">MEMORY</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">{</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 1024K</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  RAM   (xrw) : ORIGIN = 0x20000000, LENGTH = 128K</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">}</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">/* The stack grows down from the end of the RAM */</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">_estack = ORIGIN(RAM) + LENGTH(RAM);</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">SECTIONS</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">{</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  /* The vector table has to be at the very beginning of the flash */</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  .isr_vector :</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  {</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    KEEP(*(.isr_vector))</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  } &gt; FLASH</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  .text :</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  {</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    *(.text*)</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    *(.rodata*)</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    . = ALIGN(4);</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  } &gt; FLASH</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  /* Constructors of the C++ objects and functions marked as constructor */</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  .init_array :</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  {</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    . = ALIGN(4);</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    __init_array_start = .;</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    KEEP(*(.init_array*))</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    __init_array_end = .;</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  } &gt; FLASH</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  /* Start of the initial values of .data: in the flash, right after the code */</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  _sidata = LOADADDR(.data);</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  /* .data lives in the RAM (VMA), its initial values are stored in the flash (LMA) */</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  .data :</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  {</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    . = ALIGN(4);</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    _sdata = .;</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    *(.data*)</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    . = ALIGN(4);</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    _edata = .;</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  } &gt; RAM AT &gt; FLASH</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  .bss (NOLOAD) :</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  {</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    . = ALIGN(4);</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    _sbss = .;</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    *(.bss*)</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    *(COMMON)</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    . = ALIGN(4);</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    _ebss = .;</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  } &gt; RAM</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">}</span><br></div></code></pre></div></div>
<p>Прочитаймо важливі частини.</p>
<p><strong><code>MEMORY</code></strong> описує фізичні пам'яті: де вони починаються і який мають розмір. Атрибути повідомляють лінкеру, що там дозволено (<code>rx</code>: читання та виконання, <code>xrw</code>: усе).</p>
<p><strong><code>.isr_vector</code> іде першою</strong> у flash, а <code>KEEP</code> не дає лінкеру її викинути: у коді ніщо не <em>викликає</em> таблицю, тож без <code>KEEP</code> вона виглядала б невикористаною. Далі йде <code>.text</code> з кодом і даними лише для читання (<code>.rodata</code>, що означає, що <code>const</code>-змінні залишаються у flash і не займають RAM).</p>
<p><strong><code>.data</code> — найцікавіша.</strong> Подивіться на рядок <code>} &gt; RAM AT &gt; FLASH</code>. Кожна секція має дві адреси:</p>
<ul>
<li class=""><strong>VMA</strong> (virtual memory address) — де секція розташована, коли програма <em>виконується</em>: <code>.data</code> має бути в RAM, бо в неї пишуть,</li>
<li class=""><strong>LMA</strong> (load address) — де секція <em>зберігається</em> в образі прошивки: після подачі живлення RAM порожня, тож початкові значення мають лежати там, де вони виживають, — у flash.</li>
</ul>
<p><code>arm-none-eabi-objdump -h</code> показує обидві:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">Idx Name          Size      VMA       LMA       File off  Algn</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  0 .isr_vector   00000010  08000000  08000000  00001000  2**2</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  1 .text         000001b0  08000010  08000010  00001010  2**2</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  2 .init_array   00000004  080001c0  080001c0  000011c0  2**2</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  3 .data         00000004  20000000  080001c4  00002000  2**2</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  4 .bss          00000008  20000004  080001c8  00002004  2**2</span><br></div></code></pre></div></div>
<p><code>.data</code> розташована за адресою <code>0x20000000</code>, але її початкові значення лежать у flash за адресою <code>0x080001C4</code>. Хтось має їх скопіювати, і саме тому скрипт визначає символи <code>_sidata</code> (джерело у flash), <code>_sdata</code> та <code>_edata</code> (початок і кінець призначення в RAM). За цими символами немає пам'яті, це лише адреси, які C-код зчитує через <code>&amp;_sdata</code>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">080001c4 A _sidata</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">20000000 D _sdata</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">20000004 D _edata</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">20000004 B _sbss</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">2000000c B _ebss</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">20020000 R _estack</span><br></div></code></pre></div></div>
<p><strong><code>.bss</code> має <code>NOLOAD</code>.</strong> В образі вона не має вмісту, лише розмір (змінна без ініціалізатора з нульовим значенням марнувала б flash). Startup-код має заповнити нулями область між <code>_sbss</code> та <code>_ebss</code>.</p>
<p><strong><code>.init_array</code></strong> — це таблиця вказівників на конструктори. Startup-код проходить по ній і викликає їх.</p>
<p>Linker script BSP Embedbits генерується для кожного MCU за допомогою CMake із шаблону. Він має ті самі символи (<code>_sidata</code>, <code>_sdata</code>, <code>_edata</code>, <code>_sbss</code>, <code>_ebss</code>, <code>_stack_top</code>), а також описує необов'язкові регіони, наприклад CCMRAM в STM32G4, куди можна помістити секцію <code>.ccmram</code>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="startup-код">Startup-код<a href="https://embedbits.cz/uk/blog/before-main-startup-linker#startup-%D0%BA%D0%BE%D0%B4" class="hash-link" aria-label="Пряме посилання на Startup-код" title="Пряме посилання на Startup-код" translate="no">​</a></h2>
<p>Скрипт сказав, де що лежить, а тепер код робить те, чого очікує мова C:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">startup.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdint.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* Symbols created by the linker script */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">extern</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> _sidata</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> _sdata</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> _edata</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> _sbss</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> _ebss</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> _estack</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">extern</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">__init_array_start</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">extern</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">__init_array_end</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">int</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">main</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Reset_Handler</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Default_Handler</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* A few of the exception handlers, all unused ones point to the default one */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">NMI_Handler</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">       </span><span class="token keyword" style="font-style:italic">__attribute__</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">weak</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">alias</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"Default_Handler"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">HardFault_Handler</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">__attribute__</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">weak</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">alias</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"Default_Handler"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* The vector table: the first word is the initial stack pointer, the second the reset handler */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">__attribute__</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token function" style="color:rgb(130, 170, 255)">section</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">".isr_vector"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> used</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> vectorTable</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">_estack</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    Reset_Handler</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    NMI_Handler</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    HardFault_Handler</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Reset_Handler</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifdef</span><span class="token macro property"> </span><span class="token macro property expression">DEMO_DIRTY_RAM</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* Only for the demo: RAM full of garbage, as after a reset of a running system.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">     * The upper 4 kB are left alone, the stack of this function lives there. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">for</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">ramWord </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">_sdata</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> ramWord </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uintptr_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">_estack </span><span class="token operator" style="color:rgb(137, 221, 255)">-</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0x1000u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> ramWord</span><span class="token operator" style="color:rgb(137, 221, 255)">++</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">ramWord </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0xA5A5A5A5u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifndef</span><span class="token macro property"> </span><span class="token macro property expression">DEMO_SKIP_INIT</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* 1. Copy the initial values of .data from the flash to the RAM */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">source </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">_sidata</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">for</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">target </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">_sdata</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> target </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">_edata</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">target</span><span class="token operator" style="color:rgb(137, 221, 255)">++</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">source</span><span class="token operator" style="color:rgb(137, 221, 255)">++</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* 2. Zero the .bss */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">for</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">target </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">_sbss</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> target </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">_ebss</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">target</span><span class="token operator" style="color:rgb(137, 221, 255)">++</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* 3. Run the constructors */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">for</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">constructor</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> __init_array_start</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> constructor </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain"> __init_array_end</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> constructor</span><span class="token operator" style="color:rgb(137, 221, 255)">++</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">constructor</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* 4. Hand over to the application */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token function" style="color:rgb(130, 170, 255)">main</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">for</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Default_Handler</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">for</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p><code>Reset_Handler</code> має чотири кроки (частини з <code>#ifdef</code> призначені для експерименту нижче):</p>
<ol>
<li class=""><strong>Скопіювати <code>.data</code></strong> з flash (<code>_sidata</code>) в RAM (<code>_sdata</code> до <code>_edata</code>).</li>
<li class=""><strong>Обнулити <code>.bss</code></strong> (<code>_sbss</code> до <code>_ebss</code>).</li>
<li class=""><strong>Викликати конструктори</strong> з <code>.init_array</code>.</li>
<li class=""><strong>Викликати <code>main()</code></strong>. Якщо він колись повернеться, програма залишається в нескінченному циклі, бо повертатися нікуди.</li>
</ol>
<p>У BSP Embedbits модуль startup, згідно з його документацією, робить трохи більше: він також налаштовує тактування (RCC через MCAL), а потім викликає <code>AppMain()</code>. З цього порядку випливає правило: <strong>усе, що виконується до кроків 1 і 2, не повинно торкатися глобальної змінної</strong>, бо її вміст ще недійсний. Налаштування тактування — це функція, що використовує лише регістри, або компілятор тримає її стан у локальних змінних.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="експеримент-що-буде-без-цього">Експеримент: що буде без цього<a href="https://embedbits.cz/uk/blog/before-main-startup-linker#%D0%B5%D0%BA%D1%81%D0%BF%D0%B5%D1%80%D0%B8%D0%BC%D0%B5%D0%BD%D1%82-%D1%89%D0%BE-%D0%B1%D1%83%D0%B4%D0%B5-%D0%B1%D0%B5%D0%B7-%D1%86%D1%8C%D0%BE%D0%B3%D0%BE" class="hash-link" aria-label="Пряме посилання на Експеримент: що буде без цього" title="Пряме посилання на Експеримент: що буде без цього" translate="no">​</a></h2>
<p>Застосунок у прикладі має чотири глобальні змінні чотирьох видів:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">main.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> initializedValue </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0x12345678u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">       </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* .data: has a value in the flash */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> zeroedValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">                          </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* .bss:  has to be zero            */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> constantValue </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0xC0FFEE00u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* .rodata: stays in the flash      */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> constructedValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">                     </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* set by a constructor before main */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">__attribute__</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">constructor</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Construct</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    constructedValue </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0xC0DE0001u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">int</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">main</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">PrintHex</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"initializedValue"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> initializedValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">PrintHex</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"zeroedValue      "</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> zeroedValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">PrintHex</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"constantValue    "</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> constantValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">PrintHex</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"constructedValue "</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> constructedValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">PrintHex</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"address of initializedValue"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">initializedValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">PrintHex</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"address of constantValue   "</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">constantValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Quit</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>(Частина вище, з функціями, що друкують через <em>semihosting</em>, — це кілька рядків коду, які просять QEMU надрукувати текст і завершитися. Для теми вона неважлива.)</p>
<p>Спочатку звичайна збірка. Результат:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">initializedValue 0x12345678</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">zeroedValue       0x00000000</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">constantValue     0xC0FFEE00</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">constructedValue  0xC0DE0001</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">address of initializedValue 0x20000000</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">address of constantValue    0x080001BC</span><br></div></code></pre></div></div>
<p>Усі чотири значення такі, як обіцяє мова C, а адреси підтверджують історію: змінна в RAM розташована за <code>0x2000...</code>, константа у flash — за <code>0x0800...</code>.</p>
<p>Тепер експеримент. Після скидання працюючої системи RAM не порожня: вона містить те, що там було раніше. Холодний старт MCU часто «щасливий», бо RAM випадково нульова, і тоді помилка в startup-коді довго залишається прихованою. Для тесту я «забруднив» RAM (<code>-DDEMO_DIRTY_RAM</code> заповнює її <code>0xA5A5A5A5</code> перед усім іншим), а потім прибрав кроки 1 і 2 (<code>-DDEMO_SKIP_INIT</code>):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">arm-none-eabi-gcc </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-mcpu</span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain">cortex-m4 </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-mthumb</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-Os</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-ffreestanding</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-nostartfiles</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-T</span><span class="token plain"> link.ld </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-DDEMO_DIRTY_RAM</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-DDEMO_SKIP_INIT</span><span class="token plain"> startup.c main.c </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-o</span><span class="token plain"> broken.elf</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">qemu-system-arm </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-M</span><span class="token plain"> netduinoplus2 </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-nographic</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    -semihosting-config </span><span class="token assign-left variable" style="color:rgb(191, 199, 213)">enable</span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain">on,target</span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain">native </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-kernel</span><span class="token plain"> broken.elf</span><br></div></code></pre></div></div>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">initializedValue 0xA5A5A5A5</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">zeroedValue       0xA5A5A5A5</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">constantValue     0xC0FFEE00</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">constructedValue  0xC0DE0001</span><br></div></code></pre></div></div>
<p>Подивіться, що не так, а що так:</p>
<ul>
<li class=""><code>initializedValue</code> і <code>zeroedValue</code> — сміття. Перша не має початкового значення, друга не нульова, хоча обидва рядки в C-файлі стверджують інше.</li>
<li class=""><code>constantValue</code> у порядку, бо вона у flash, і її нікому не потрібно копіювати.</li>
<li class=""><code>constructedValue</code> у порядку, бо конструктор записав її <em>після</em> (відсутньої) ініціалізації. Ось чому цей вид помилок так важко знайти: одна половина програми працює, а друга має випадкові значення.</li>
</ul>
<p>З брудною RAM і повним startup-кодом та сама програма знову друкує правильні значення.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="як-це-виглядає-в-реальному-житті">Як це виглядає в реальному житті<a href="https://embedbits.cz/uk/blog/before-main-startup-linker#%D1%8F%D0%BA-%D1%86%D0%B5-%D0%B2%D0%B8%D0%B3%D0%BB%D1%8F%D0%B4%D0%B0%D1%94-%D0%B2-%D1%80%D0%B5%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83-%D0%B6%D0%B8%D1%82%D1%82%D1%96" class="hash-link" aria-label="Пряме посилання на Як це виглядає в реальному житті" title="Пряме посилання на Як це виглядає в реальному житті" translate="no">​</a></h2>
<p>Кілька симптомів і те, що зазвичай за ними стоїть:</p>





























<table><thead><tr><th>Симптом</th><th>Імовірна причина</th></tr></thead><tbody><tr><td>Програма потрапляє в hard fault одразу після скидання</td><td>Перше слово таблиці векторів не є дійсною адресою стека (неправильний <code>_estack</code>, неправильний розмір RAM) або таблиця не на початку flash</td></tr><tr><td>Глобальна змінна з ініціалізатором має неправильне значення, і та, що з <code>= 0</code>, також</td><td><code>.data</code> не скопійовано або <code>.bss</code> не обнулено (неправильний символ, секція, якої немає в скрипті)</td></tr><tr><td>Працює після подачі живлення і ламається після скидання</td><td>Те саме, при подачі живлення RAM випадково була порожньою</td></tr><tr><td>Дивне значення, що змінюється разом з розміром коду</td><td>Стек росте вниз до <code>.bss</code> / <code>.data</code>, переповнення стека</td></tr><tr><td>Перший <code>printf</code> або драйвер не працює</td><td>Код, що виконується до кроків 1 і 2, використовує глобальну змінну</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="що-кажуть-вам-числа-інструмента">Що кажуть вам числа інструмента<a href="https://embedbits.cz/uk/blog/before-main-startup-linker#%D1%89%D0%BE-%D0%BA%D0%B0%D0%B6%D1%83%D1%82%D1%8C-%D0%B2%D0%B0%D0%BC-%D1%87%D0%B8%D1%81%D0%BB%D0%B0-%D1%96%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%B0" class="hash-link" aria-label="Пряме посилання на Що кажуть вам числа інструмента" title="Пряме посилання на Що кажуть вам числа інструмента" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">   text	   data	    bss	    dec	    hex	filename</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    448	      8	      8	    464	    1d0	good.elf</span><br></div></code></pre></div></div>
<ul>
<li class=""><strong>Flash</strong> = <code>text</code> + <code>data</code>. Початкові значення <code>.data</code> також займають місце у flash, тож великий ініціалізований масив коштує вам вдвічі: у flash і в RAM. Масив, який не змінюється, має бути <code>const</code>, і тоді він лише у flash.</li>
<li class=""><strong>RAM</strong> = <code>data</code> + <code>bss</code> (+ стек і купа).</li>
</ul>
<p>Лінкер також може перевірити це за вас: <code>-Wl,--print-memory-usage</code> друкує, скільки кожного регіону використано:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">Memory region         Used Size  Region Size  %age Used</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">           FLASH:         456 B         1 MB      0.04%</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">             RAM:          12 B       128 KB      0.01%</span><br></div></code></pre></div></div>
<p>Додайте цей прапорець до своєї збірки й читайте файл <code>.map</code> (<code>-Wl,-Map,out.map</code>), коли щось опиняється в несподіваному місці: він містить адресу кожної функції та кожної змінної.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="підсумок">Підсумок<a href="https://embedbits.cz/uk/blog/before-main-startup-linker#%D0%BF%D1%96%D0%B4%D1%81%D1%83%D0%BC%D0%BE%D0%BA" class="hash-link" aria-label="Пряме посилання на Підсумок" title="Пряме посилання на Підсумок" translate="no">​</a></h2>
<ol>
<li class="">Після скидання ядро зчитує два слова: вказівник стека й адресу обробника скидання. Усе інше — ваш код.</li>
<li class=""><strong>Linker script</strong> — це карта: де пам'яті, в якому порядку лежать секції та які символи позначають їхні межі.</li>
<li class=""><strong>Startup-код</strong> робить обіцянки мови правдою: він копіює <code>.data</code>, обнуляє <code>.bss</code>, запускає конструктори і викликає <code>main()</code>.</li>
<li class="">Змінна, яка має значення у flash і живе в RAM, має дві адреси: адресу завантаження та адресу виконання. Це ключ до всієї теми.</li>
<li class="">Тримайте <code>-Wl,--print-memory-usage</code> увімкненим і читайте <code>arm-none-eabi-size</code> та map-файл. Вони розкажуть вам про те, куди поділася ваша пам'ять, більше за налагоджувач.</li>
</ol>
<p>У BSP Embedbits ви не пишете ці два файли самі: linker script генерується для вибраного MCU, а startup-код постачається з гілкою BSP для сімейства. Але розуміти, що вони роблять, усе одно варто, бо коли прошивка не стартує, ніхто інший вам не допоможе.</p>]]></content:encoded>
            <category>Вбудовані системи</category>
            <category>C</category>
            <category>STM32</category>
        </item>
        <item>
            <title><![CDATA[Unit-тестування вбудованого C на вашому ПК за допомогою Unity і CMock]]></title>
            <link>https://embedbits.cz/uk/blog/unit-testing-unity-cmock</link>
            <guid>https://embedbits.cz/uk/blog/unit-testing-unity-cmock</guid>
            <pubDate>Mon, 05 Oct 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[«Вбудоване ПЗ неможливо тестувати unit-тестами, для цього потрібна апаратна частина». Я чую це часто, і це правда рівно для одного виду коду: того, що торкається регістрів. Усе решта — скінченні автомати, парсери протоколів, логіка керування, перетворення значень — це звичайний C, який компілюється на вашому ПК. А на вашому ПК він виконується за мілісекунди, без налагоджувача, без кабелю і без прошивання.]]></description>
            <content:encoded><![CDATA[<p>«Вбудоване ПЗ неможливо тестувати unit-тестами, для цього потрібна апаратна частина». Я чую це часто, і це правда рівно для одного виду коду: того, що торкається регістрів. Усе решта — скінченні автомати, парсери протоколів, логіка керування, перетворення значень — це звичайний C, який компілюється на вашому ПК. А на вашому ПК він виконується за мілісекунди, без налагоджувача, без кабелю і без прошивання.</p>
<p>Ця стаття показує, як тестувати модуль за допомогою <a href="https://github.com/ThrowTheSwitch/Unity" target="_blank" rel="noopener noreferrer" class="">Unity</a> (тестовий фреймворк) і <a href="https://github.com/ThrowTheSwitch/CMock" target="_blank" rel="noopener noreferrer" class="">CMock</a> (генератор моків) — від дизайну, який це уможливлює, до файлу CMake, що все збирає. Усе було зібрано й запущено, а вивід нижче справжній.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="що-потрібно-тестувати-і-що-потрібно-прибрати">Що потрібно тестувати і що потрібно прибрати<a href="https://embedbits.cz/uk/blog/unit-testing-unity-cmock#%D1%89%D0%BE-%D0%BF%D0%BE%D1%82%D1%80%D1%96%D0%B1%D0%BD%D0%BE-%D1%82%D0%B5%D1%81%D1%82%D1%83%D0%B2%D0%B0%D1%82%D0%B8-%D1%96-%D1%89%D0%BE-%D0%BF%D0%BE%D1%82%D1%80%D1%96%D0%B1%D0%BD%D0%BE-%D0%BF%D1%80%D0%B8%D0%B1%D1%80%D0%B0%D1%82%D0%B8" class="hash-link" aria-label="Пряме посилання на Що потрібно тестувати і що потрібно прибрати" title="Пряме посилання на Що потрібно тестувати і що потрібно прибрати" translate="no">​</a></h2>
<p>Unit-тест запускає один модуль в ізоляції. Модуль, що тестується, викликає нижній шар (BSP, драйвер, інший модуль), і <strong>саме в цьому</strong> проблема: нижньому шару потрібна апаратна частина. Рішення — не давати модулю симульовану апаратну частину, а замінити весь нижній шар чимось, чим керує тест. Це «щось» — <strong>мок</strong> (mock): функція з тим самим сигнатурою, що й справжня, яка не робить нічого справжнього. Вона лише запам'ятовує, як її викликали, і повертає те, що їй наказав повернути тест.</p>
<p>Це працює лише тоді, коли в модулі є місце, де нижній шар можна замінити. У статті про <a class="" href="https://embedbits.cz/uk/blog/solid-principles-c-cpp">принципи SOLID</a> йшлося про <em>link-time injection</em>: модуль викликає функцію, оголошену в заголовку (<code>Bsp_Thermostat.h</code>), а система збірки вирішує, який вихідний файл її реалізує. На цільовій платформі це справжній BSP, в unit-тесті — мок. Це той самий принцип, що й в архітектурі зі статті про <a class="" href="https://embedbits.cz/uk/blog/design-architecture">дизайн і архітектуру</a>: коли шари мають чисті інтерфейси, кожен шар можна тестувати без шарів під ним.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="інструменти">Інструменти<a href="https://embedbits.cz/uk/blog/unit-testing-unity-cmock#%D1%96%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%B8" class="hash-link" aria-label="Пряме посилання на Інструменти" title="Пряме посилання на Інструменти" translate="no">​</a></h2>
<ul>
<li class=""><strong>Unity</strong> — фреймворк з перевірками (<code>TEST_ASSERT_TRUE</code>, <code>TEST_ASSERT_EQUAL_UINT8</code>, <code>TEST_ASSERT_EQUAL_FLOAT</code>, <code>TEST_ASSERT_EQUAL_MEMORY</code> та багато інших) і невеликий скрипт, що генерує <code>main()</code> зі списком тестів з вашого тестового файлу, тож вам ніколи не доведеться реєструвати тест вручну. Це кілька C-файлів, які компілюються разом із тестом.</li>
<li class=""><strong>CMock</strong> читає заголовковий файл і генерує мок кожної функції в ньому: <code>MockBsp_Thermostat.c/.h</code>. Він написаний на Ruby, але на Ruby написаний лише <em>генератор</em>, а згенерований мок — звичайний C.</li>
<li class=""><strong>Ruby</strong> потрібен для двох генераторів, які виконуються під час збірки. У тестований код він не потрапляє.</li>
</ul>
<p>На платформі Embedbits ці три інструменти — артефакти (<code>unity</code>, <code>cmock</code> і <code>ruby</code>), тож проєкт не залежить від того, що встановлено на ПК розробника. Версії зберігаються в <code>ArtifactsConfig.txt</code> (синтаксис <code>&lt;artifact_name&gt;;&lt;binary_version&gt;;&lt;handler_version&gt;</code>). У цій статті я використав Unity 2.6.1 та CMock 2.6.0 зі звичайним CMake, щоб приклад працював усюди.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="модуль-що-тестується">Модуль, що тестується<a href="https://embedbits.cz/uk/blog/unit-testing-unity-cmock#%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C-%D1%89%D0%BE-%D1%82%D0%B5%D1%81%D1%82%D1%83%D1%94%D1%82%D1%8C%D1%81%D1%8F" class="hash-link" aria-label="Пряме посилання на Модуль, що тестується" title="Пряме посилання на Модуль, що тестується" translate="no">​</a></h2>
<p>Термостат із гістерезисом. Він зчитує температуру з BSP, вмикає нагрівач нижче 20 °C і вимикає вище 22 °C. Якщо датчик не відповідає, він вимикає нагрівач, бо нагрівач без вимірювання — це пожежа. Інтерфейс до апаратної частини — заголовок із двома функціями:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Bsp_Thermostat.h</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifndef</span><span class="token macro property"> </span><span class="token macro property expression">BSP_THERMOSTAT_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">BSP_THERMOSTAT_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdbool.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* Implemented by the BSP of the board - or mocked in the unit test. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* Returns false if the sensor does not answer, the value is then not valid. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">bool </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Get_Celsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">celsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Set_Heater</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">bool on</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><br></div></code></pre></div></div>
<p>Публічний порт модуля та його реалізація:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Thermostat_Port.h</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifndef</span><span class="token macro property"> </span><span class="token macro property expression">THERMOSTAT_THERMOSTAT_PORT_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">THERMOSTAT_THERMOSTAT_PORT_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdbool.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Init</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Task</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">bool </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Is_HeaterOn</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><br></div></code></pre></div></div>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Thermostat.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Thermostat_Port.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Bsp_Thermostat.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">THERMOSTAT_SWITCH_ON_CELSIUS</span><span class="token macro property">   </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression"> </span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">20.0f</span><span class="token macro property expression"> </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">THERMOSTAT_SWITCH_OFF_CELSIUS</span><span class="token macro property">  </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression"> </span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">22.0f</span><span class="token macro property expression"> </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> bool isHeaterOn</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Set_Heater</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">bool on</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    isHeaterOn </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> on</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Set_Heater</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">on</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Init</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Set_Heater</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">false</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Task</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> celsius </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0.0f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">!</span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Get_Celsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">celsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Set_Heater</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">false</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">          </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* fail safe: no value, no heating */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">else</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">celsius </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain"> THERMOSTAT_SWITCH_ON_CELSIUS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Set_Heater</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">true</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">else</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">celsius </span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain"> THERMOSTAT_SWITCH_OFF_CELSIUS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Set_Heater</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">false</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">else</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* inside the hysteresis band: keep the current state */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">bool </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Is_HeaterOn</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> isHeaterOn</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Модуль не підключає жодного апаратного заголовка, нічого не знає про STM32 і може бути скомпільований компілятором вашого ПК. Це перший тест дизайну: якщо він не компілюється, модуль відокремлено недостатньо.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="тест">Тест<a href="https://embedbits.cz/uk/blog/unit-testing-unity-cmock#%D1%82%D0%B5%D1%81%D1%82" class="hash-link" aria-label="Пряме посилання на Тест" title="Пряме посилання на Тест" translate="no">​</a></h2>
<p>Тестовий файл підключає заголовок модуля, що тестується, і заголовок <strong>моку</strong>, якого ще не існує. Його генерують із <code>Bsp_Thermostat.h</code> під час збірки.</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">test_Thermostat.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"unity.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Thermostat_Port.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"MockBsp_Thermostat.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">setUp</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Set_Heater_Expect</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">false</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Init</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">tearDown</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* The mock reads this variable when Thermostat_Task() calls it, so it has to outlive the helper. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> sensorCelsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* Helper: the sensor answers with the given temperature. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Sensor_Returns</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> celsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    sensorCelsius </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> celsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Get_Celsius_ExpectAnyArgsAndReturn</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">true</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Get_Celsius_ReturnThruPtr_celsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">sensorCelsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">test_HeaterSwitchesOnBelowLowerLimit</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Sensor_Returns</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">18.0f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Set_Heater_Expect</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">true</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Task</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">TEST_ASSERT_TRUE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Is_HeaterOn</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">test_HeaterSwitchesOffAboveUpperLimit</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Sensor_Returns</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">18.0f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Set_Heater_Expect</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">true</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Task</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Sensor_Returns</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">23.0f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Set_Heater_Expect</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">false</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Task</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">TEST_ASSERT_FALSE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Is_HeaterOn</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">test_HeaterKeepsStateInsideHysteresis</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Sensor_Returns</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">18.0f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Set_Heater_Expect</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">true</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Task</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Sensor_Returns</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">21.0f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">          </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* no Bsp_Set_Heater expected: the mock fails the test if it is called */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Task</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">TEST_ASSERT_TRUE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Is_HeaterOn</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">test_HeaterSwitchesOffWhenSensorFails</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Sensor_Returns</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">18.0f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Set_Heater_Expect</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">true</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Task</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Get_Celsius_ExpectAnyArgsAndReturn</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">false</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Set_Heater_Expect</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">false</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Task</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">TEST_ASSERT_FALSE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Is_HeaterOn</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Прочитаймо його згори.</p>
<ul>
<li class=""><code>setUp()</code> викликається Unity перед кожним тестом. Модуль зберігає свій стан у <code>static</code>-змінній, тож кожен тест починається з <code>Thermostat_Init()</code>, а <code>Bsp_Set_Heater_Expect(false)</code> повідомляє моку, що ініціалізація вимикає нагрівач. Без цього скидання тести залежали б одне від одного та від порядку їх виконання.</li>
<li class=""><code>Sensor_Returns()</code> — допоміжна функція, яка каже: «наступний виклик <code>Bsp_Get_Celsius()</code> повертає <code>true</code> і встановлює значення в цю температуру». <code>_ExpectAnyArgsAndReturn</code> означає «мені байдуже, який вказівник», а <code>_ReturnThruPtr_celsius</code> заповнює вихідний параметр.</li>
<li class="">Кожен тест — це коротка історія з трьох частин: датчик щось повідомляє, модуль запускається (<code>Thermostat_Task()</code>), результат перевіряється. Очікування <code>Bsp_Set_Heater_Expect(true)</code> також є перевіркою: якщо модуль викличе функцію з іншим значенням або не викличе її взагалі, тест впаде.</li>
<li class="">Третій тест показує силу моків. Усередині смуги гістерезису нічого не очікується, і це і є перевірка: мок валить тест, щойно модуль викликає <code>Bsp_Set_Heater()</code>.</li>
</ul>
<p>Пастка, про яку варто знати: <code>_ReturnThruPtr_celsius(&amp;value)</code> не копіює значення в момент виклику, а запам'ятовує <strong>вказівник</strong> і зчитує значення, коли модуль викликає мок. Якщо <code>value</code> — локальна змінна допоміжної функції, до того часу її вже не існує. Перша версія прикладу для цієї статті мала допоміжну функцію, написану саме так, і результатом стали чотири впалі тести з повідомленням, що виглядає як помилка в модулі:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">test_HeaterSwitchesOnBelowLowerLimit:FAIL: Expected 1 Was 0. Function Bsp_Set_Heater</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">Argument on. Function called with unexpected argument value.</span><br></div></code></pre></div></div>
<p>Модуль був правильним, а змінна — сміттям. Ось чому в прикладі є <code>static float sensorCelsius</code> з коментарем.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="збірка">Збірка<a href="https://embedbits.cz/uk/blog/unit-testing-unity-cmock#%D0%B7%D0%B1%D1%96%D1%80%D0%BA%D0%B0" class="hash-link" aria-label="Пряме посилання на Збірка" title="Пряме посилання на Збірка" translate="no">​</a></h2>
<p>Два генератори й один виконуваний файл. CMake запускає генератори перед компіляцією, а зміна заголовка перегенеровує мок:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">cmock.yml</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token key atrule">:cmock</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token key atrule">:mock_path</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> mocks</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token key atrule">:plugins</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain">ignore</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain">expect_any_args</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain">return_thru_ptr</span><br></div></code></pre></div></div>
<div class="language-cmake codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">CMakeLists.txt</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cmake codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">cmake_minimum_required</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token property">VERSION</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">3.19</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">project</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">ThermostatTests C</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">enable_testing</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">find_program</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">RUBY ruby REQUIRED</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">UNITY_DIR  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_SOURCE_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/../unity</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">CMOCK_DIR  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_SOURCE_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/../cmock</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">MOCK_DIR   </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_BINARY_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/mocks</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">TEST_NAME  test_Thermostat</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># 1. mock of the BSP interface, generated from its header</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">add_custom_command</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    OUTPUT  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">MOCK_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/MockBsp_Thermostat.c </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">MOCK_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/MockBsp_Thermostat.h</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    COMMAND </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">RUBY</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">CMOCK_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/lib/cmock.rb -o</span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_SOURCE_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/cmock.yml</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_SOURCE_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/src/Bsp_Thermostat.h</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">WORKING_DIRECTORY</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_BINARY_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">DEPENDS</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_SOURCE_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/src/Bsp_Thermostat.h </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_SOURCE_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/cmock.yml</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># 2. the test runner (main + list of the tests), generated from the test file</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">add_custom_command</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    OUTPUT  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_BINARY_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/</span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">TEST_NAME</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">_Runner.c</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    COMMAND </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">RUBY</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">UNITY_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/auto/generate_test_runner.rb</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_SOURCE_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/test/</span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">TEST_NAME</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">.c </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_BINARY_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/</span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">TEST_NAME</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">_Runner.c</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token property">DEPENDS</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_SOURCE_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/test/</span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">TEST_NAME</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">.c</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># 3. one test executable: the module under test + the mock + the test + the framework</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">add_executable</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">TEST_NAME</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    src/Thermostat.c</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    test/</span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">TEST_NAME</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">.c</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_BINARY_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/</span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">TEST_NAME</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">_Runner.c</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">MOCK_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/MockBsp_Thermostat.c</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">UNITY_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/src/unity.c</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">CMOCK_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/src/cmock.c</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">target_include_directories</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">TEST_NAME</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token namespace" style="color:rgb(178, 204, 214)">PRIVATE</span><span class="token plain"> src </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">MOCK_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">UNITY_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/src </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">CMOCK_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/src</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">target_compile_options</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">TEST_NAME</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token namespace" style="color:rgb(178, 204, 214)">PRIVATE</span><span class="token plain"> -Wall -Wextra</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">add_test</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token property">NAME</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">TEST_NAME</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> COMMAND </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">TEST_NAME</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><br></div></code></pre></div></div>
<p>Конфігурація CMock (<code>cmock.yml</code>) містить лише список плагінів: <code>ignore</code>, <code>expect_any_args</code> і <code>return_thru_ptr</code>, які дають функції <code>_Ignore</code>, <code>_ExpectAnyArgs</code> та <code>_ReturnThruPtr_</code>, що використовує тест вище. <code>mock_path</code> задається відносно папки, з якої запускається генератор, тобто каталогу збірки.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">cmake </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-S</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">.</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-B</span><span class="token plain"> build</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">cmake </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">--build</span><span class="token plain"> build</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">./build/test_Thermostat        </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># or: ctest --test-dir build</span><br></div></code></pre></div></div>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">test_Thermostat.c:26:test_HeaterSwitchesOnBelowLowerLimit:PASS</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">test_Thermostat.c:36:test_HeaterSwitchesOffAboveUpperLimit:PASS</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">test_Thermostat.c:49:test_HeaterKeepsStateInsideHysteresis:PASS</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">test_Thermostat.c:61:test_HeaterSwitchesOffWhenSensorFails:PASS</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">-----------------------</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">4 Tests 0 Failures 0 Ignored</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">OK</span><br></div></code></pre></div></div>
<p>Зверніть увагу, чого у виконуваному файлі <em>немає</em>: ні startup-коду, ні linker script, ні HAL, ні заголовків ST. Лише модуль, мок, тест і фреймворк. Він збирається за пару секунд і виконується за мілісекунди, тож його можна запускати при кожному збереженні та кожному коміті.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="чи-справді-тест-щось-тестує">Чи справді тест щось тестує?<a href="https://embedbits.cz/uk/blog/unit-testing-unity-cmock#%D1%87%D0%B8-%D1%81%D0%BF%D1%80%D0%B0%D0%B2%D0%B4%D1%96-%D1%82%D0%B5%D1%81%D1%82-%D1%89%D0%BE%D1%81%D1%8C-%D1%82%D0%B5%D1%81%D1%82%D1%83%D1%94" class="hash-link" aria-label="Пряме посилання на Чи справді тест щось тестує?" title="Пряме посилання на Чи справді тест щось тестує?" translate="no">​</a></h2>
<p>Тест, який не може впасти, нічого не вартий. Найдешевша перевірка — <strong>мутація</strong>: навмисно зламати код і подивитися, чи помітить це якийсь тест. Я змінив верхню межу з 22 °C на 20 °C, що прибирає гістерезис:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">test_HeaterSwitchesOnBelowLowerLimit:PASS</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">test_HeaterSwitchesOffAboveUpperLimit:PASS</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">test_HeaterKeepsStateInsideHysteresis:FAIL:Function Bsp_Set_Heater.  Called more times than expected.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">test_HeaterSwitchesOffWhenSensorFails:PASS</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">4 Tests 1 Failures 0 Ignored</span><br></div></code></pre></div></div>
<p>Тест гістерезису виявив помилку, а повідомлення каже, що сталося: модуль викликав <code>Bsp_Set_Heater()</code>, коли не мав. Корисна звичка — робити так один раз для кожного нового тесту.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="що-мокати-а-що-ні">Що мокати, а що ні<a href="https://embedbits.cz/uk/blog/unit-testing-unity-cmock#%D1%89%D0%BE-%D0%BC%D0%BE%D0%BA%D0%B0%D1%82%D0%B8-%D0%B0-%D1%89%D0%BE-%D0%BD%D1%96" class="hash-link" aria-label="Пряме посилання на Що мокати, а що ні" title="Пряме посилання на Що мокати, а що ні" translate="no">​</a></h2>
<ul>
<li class=""><strong>Мокайте межу модуля, а не його нутрощі.</strong> Мок замінює інтерфейс нижнього шару (тут <code>Bsp_Thermostat.h</code>). Не мокайте власні допоміжні функції в тому самому модулі: тоді тест описує, як написаний код, і ламається при кожному рефакторингу, навіть коли поведінка та сама.</li>
<li class=""><strong>Тестуйте через публічний порт.</strong> Тест підключає <code>Thermostat_Port.h</code> і більше нічого з модуля. Якщо для тестування чогось потрібен внутрішній заголовок, це ознака того, що модуль має дві відповідальності. Про те, як розділяються публічні та внутрішні файли, читайте у статті про <a class="" href="https://embedbits.cz/uk/blog/file-organization-embedded-c">організацію файлів</a>.</li>
<li class=""><strong>Одна поведінка — один тест.</strong> Назва каже, чого очікується (<code>HeaterSwitchesOffWhenSensorFails</code>), тож червоний тест повідомляє, що зламалося, ще до того, як ви його відкриєте.</li>
<li class=""><strong>Той самий шаблон працює на кожному шарі.</strong> Модуль застосунку тестується з моком інтерфейсу BSP, модуль MCAL — з моком порту RAL. У кожному випадку нижній шар замінюється, а шар, що тестується, залишається справжнім.</li>
<li class=""><strong>Порядок викликів.</strong> CMock може перевіряти, що виклики різних моків відбуваються в заданому порядку (опція <code>:enforce_strict_ordering</code>). Це корисно для послідовностей ініціалізації, але використовуйте її лише там, де порядок є вимогою, а не випадковістю реалізації.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="чого-host-тест-не-скаже">Чого host-тест не скаже<a href="https://embedbits.cz/uk/blog/unit-testing-unity-cmock#%D1%87%D0%BE%D0%B3%D0%BE-host-%D1%82%D0%B5%D1%81%D1%82-%D0%BD%D0%B5-%D1%81%D0%BA%D0%B0%D0%B6%D0%B5" class="hash-link" aria-label="Пряме посилання на Чого host-тест не скаже" title="Пряме посилання на Чого host-тест не скаже" translate="no">​</a></h2>
<p>Я не хочу продавати host-тести як усе. Вони не знаходять:</p>
<ul>
<li class=""><strong>таймінги</strong>, переривання та гонки між ними,</li>
<li class=""><strong>регістри</strong>: що пін справді піднявся у високий рівень, що тактування периферії було ввімкнено. Це завдання коду в RAL і MCAL, і для нього потрібен симулятор або справжня апаратна частина,</li>
<li class=""><strong>відмінності між ПК і MCU</strong>: ширину <code>int</code>, вирівнювання, оптимізатор компілятора цільової платформи. Тести на ПК показують, що логіка правильна, а не що правильна прошивка.</li>
</ul>
<p>Ось чому host-тести — це основа піраміди: їх багато, вони швидкі й запускаються на кожен коміт. Над ними стоять симулятор (наприклад, Renode) і апаратні тести, яких менше й які повільніші та покривають те, чого ПК не може.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="підсумок">Підсумок<a href="https://embedbits.cz/uk/blog/unit-testing-unity-cmock#%D0%BF%D1%96%D0%B4%D1%81%D1%83%D0%BC%D0%BE%D0%BA" class="hash-link" aria-label="Пряме посилання на Підсумок" title="Пряме посилання на Підсумок" translate="no">​</a></h2>
<ol>
<li class="">Проєктуйте модуль так, щоб усе під ним було за заголовком (<em>seam</em>).</li>
<li class="">Нехай CMock згенерує мок із цього заголовка, а Unity — runner із тестового файлу.</li>
<li class="">Зберіть один маленький виконуваний файл для ПК: модуль, мок, тест, фреймворк.</li>
<li class="">Пишіть тести як історії: мок каже, як виглядає світ, модуль діє, перевірка підтверджує результат.</li>
<li class="">Один раз навмисно зламайте код, щоб побачити, що тест падає.</li>
</ol>
<p>Увесь приклад займає близько 150 рядків коду, і наступного разу, коли хтось запитає вас, чи справді термостат вимикає нагрівач, коли датчик виходить з ладу, вам не потрібен нагрівач. Ви запускаєте <code>ctest</code>.</p>]]></content:encoded>
            <category>Вбудовані системи</category>
            <category>C</category>
            <category>Тестування</category>
        </item>
        <item>
            <title><![CDATA[Організація файлів у вбудованому C: як тека стає модулем]]></title>
            <link>https://embedbits.cz/uk/blog/file-organization-embedded-c</link>
            <guid>https://embedbits.cz/uk/blog/file-organization-embedded-c</guid>
            <pubDate>Sun, 04 Oct 2026 14:00:00 GMT</pubDate>
            <description><![CDATA[Мій стиль кодування каже, що імена файлів починаються з імені модуля і що «повна організація файлів описана в іншому місці». Ось це інше місце.]]></description>
            <content:encoded><![CDATA[<p>Мій <a class="" href="https://embedbits.cz/uk/docs/platform/embi-platform/coding-style">стиль кодування</a> каже, що імена файлів починаються з імені модуля і що «повна організація файлів описана в іншому місці». Ось це інше місце.</p>
<p>У C немає <code>private</code>, просторів імен і пакетів. Щойно код розділено на файли, файли та система збірки — <strong>єдині</strong> інструменти, які в нас є, щоб сказати, що належить разом, що є публічним, а що нікого більше не стосується. Тож організація файлів — це не питання смаку, а частина дизайну. Ця стаття описує, як я організовую вбудований проєкт на трьох рівнях: проєкт, модуль і окремий файл.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="рівень-1-проєкт">Рівень 1: проєкт<a href="https://embedbits.cz/uk/blog/file-organization-embedded-c#%D1%80%D1%96%D0%B2%D0%B5%D0%BD%D1%8C-1-%D0%BF%D1%80%D0%BE%D1%94%D0%BA%D1%82" class="hash-link" aria-label="Пряме посилання на Рівень 1: проєкт" title="Пряме посилання на Рівень 1: проєкт" translate="no">​</a></h2>
<p>Проєкт, створений за допомогою EmBi_Platform, щоразу має однакову структуру:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">Project_root/</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">├── Application/          your application</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">│   ├── AppMain/            entry point</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">│   ├── AppCore/            top level logic</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">│   ├── AppFun/             functionalities (high level logic)</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">│   ├── AppComp/            components (low level logic)</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">│   └── AppCom/             communication modules</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">├── Middlewares/          reusable software without hardware dependency</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">│   ├── ThirdParty/         vendor sources, one Git submodule per component</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">│   ├── &lt;Name&gt;/             your glue code and configuration of that component</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">│   └── Middlewares.cmake</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">├── Bsp/                  Board Support Package</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">│   ├── Hal/  Mcal/  Ral/   the three abstraction layers</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">│   ├── Linker/  Startup/   linker script generator, startup code</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">│   └── Docs/</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">├── EmBi_Platform/        build tooling (Git submodule), not part of the firmware</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">├── STM32CubeIDE/         generated IDE project</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">├── ArtifactsConfig.txt   versions of the build tools</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">└── CMakeLists.txt        project root build file</span><br></div></code></pre></div></div>
<p>Кожна тека відповідає на одне питання, і ця відповідь — причина, чому код живе саме там.</p>
<p><strong>Application</strong> — це те, що відрізняє продукт від усіх інших продуктів на тому самому MCU. Він має власні внутрішні шари, і їхні назви — це рівні «дерева» зі <a class="" href="https://embedbits.cz/uk/blog/design-architecture">статті про архітектуру</a>: <code>AppMain</code> усе запускає, <code>AppCore</code> — верхівка дерева з логікою всього пристрою, <code>AppFun</code> містить окремі функціональності, <code>AppComp</code> — низькорівневі компоненти, з яких вони складені, а <code>AppCom</code> — комунікацію із зовнішнім світом. Виклики йдуть згори вниз, ніколи навпаки.</p>
<p><strong>Middlewares</strong> мають два місця для кожного компонента, і це зроблено навмисно. <code>ThirdParty/FreeRTOS</code> — це код вендора, підключений як Git submodule у вибраній вами версії, і його <strong>ніколи не редагують</strong>. Усе, що є вашим (port-шар, конфігурація), живе поруч у <code>Middlewares/FreeRTOS</code> і не перезаписується, коли ви переходите на іншу версію коду вендора. Коли вендор випускає виправлення, ви оновлюєте submodule, а не пропатчену копію.</p>
<p><strong>Bsp</strong> описано в <a class="" href="https://embedbits.cz/uk/docs/bsp">документації BSP</a>, а коротка версія така: <code>Ral</code> знає регістри, <code>Mcal</code> — периферію, а <code>Hal</code> — плату. Кожне сімейство STM32 має власну гілку BSP, тому теки виглядають однаково для кожного MCU.</p>
<p><strong>EmBi_Platform</strong> і <strong>STM32CubeIDE</strong> — це інструменти. Вони не потрапляють у бінарний файл, а проєкт IDE генерується, тож його можна викинути й створити заново.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="рівень-2-модуль">Рівень 2: модуль<a href="https://embedbits.cz/uk/blog/file-organization-embedded-c#%D1%80%D1%96%D0%B2%D0%B5%D0%BD%D1%8C-2-%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C" class="hash-link" aria-label="Пряме посилання на Рівень 2: модуль" title="Пряме посилання на Рівень 2: модуль" translate="no">​</a></h2>
<p>Модуль — це тека з фіксованим набором файлів. Створювати їх вручну не потрібно, платформа генерує їх сама (див. кінець статті), і результат виглядає так:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">Temperature/</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">├── CMakeLists.txt</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">├── Temperature_Types.h</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">├── Temperature_Port.h</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">├── Temperature.h</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">├── Temperature.c</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">└── Temperature_Filter.c     (a component, see below)</span><br></div></code></pre></div></div>



































<table><thead><tr><th>Файл</th><th>Роль</th><th>Хто може його підключати</th></tr></thead><tbody><tr><td><code>Temperature_Types.h</code></td><td>Публічні типи, перелічення та макроси</td><td>усі</td></tr><tr><td><code>Temperature_Port.h</code></td><td>Публічні функції: <strong>єдина</strong> точка входу в модуль</td><td>усі</td></tr><tr><td><code>Temperature.h</code></td><td>Внутрішні типи та функції, спільні для файлів модуля</td><td>лише сам модуль</td></tr><tr><td><code>Temperature.c</code></td><td>Реалізація</td><td>(вихідний файл)</td></tr><tr><td><code>CMakeLists.txt</code></td><td>Створює статичну бібліотеку <code>Temperature_Lib</code></td><td>збірка</td></tr></tbody></table>
<p>Ідея в тому, що користувач модуля читає <strong>два</strong> файли і більше нічого. Перший — це типи (згенерований файл також містить тип версії та звичайні перелічення для станів запиту й функції, тут я скоротив його до того, що потрібно для прикладу):</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Temperature_Types.h</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \file Temperature_Types.h</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \ingroup Temperature</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \brief Temperature module global types definition</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifndef</span><span class="token macro property"> </span><span class="token macro property expression">TEMPERATURE_TEMPERATURE_TYPES_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">TEMPERATURE_TEMPERATURE_TYPES_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ============================== INCLUDES ================================== */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdint.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ============================== TYPEDEFS ================================== */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/** Temperature in tenths of a degree Celsius, 253 = 25.3 degC */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">int16_t</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">temperature_Value_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/** Enumeration used to signal request processing state */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">enum</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    TEMPERATURE_REQUEST_ERROR </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**&lt; Processing request failed  */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    TEMPERATURE_REQUEST_OK          </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**&lt; Processing request succeed */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token class-name" style="color:rgb(255, 203, 107)">temperature_RequestState_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><span class="token macro property"> </span><span class="token macro property comment" style="color:rgb(105, 112, 152);font-style:italic">/* TEMPERATURE_TEMPERATURE_TYPES_H */</span><br></div></code></pre></div></div>
<p>Другий — це порт, який є всім інтерфейсом модуля. Усього, чого тут немає, для інших не існує:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Temperature_Port.h</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \file Temperature_Port.h</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \ingroup Temperature</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \brief Temperature module public functionality</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> *</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * The only header that other modules include.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifndef</span><span class="token macro property"> </span><span class="token macro property expression">TEMPERATURE_TEMPERATURE_PORT_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">TEMPERATURE_TEMPERATURE_PORT_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifdef</span><span class="token macro property"> </span><span class="token macro property expression">__cplusplus</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">extern</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"C"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ============================== INCLUDES ================================== */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Temperature_Types.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ========================= EXPORTED FUNCTIONS ============================= */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain">                       </span><span class="token function" style="color:rgb(130, 170, 255)">Temperature_Init</span><span class="token plain">     </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain">                       </span><span class="token function" style="color:rgb(130, 170, 255)">Temperature_Task</span><span class="token plain">     </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token class-name" style="color:rgb(255, 203, 107)">temperature_RequestState_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Temperature_Get_Value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">temperature_Value_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> value </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifdef</span><span class="token macro property"> </span><span class="token macro property expression">__cplusplus</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><span class="token macro property"> </span><span class="token macro property comment" style="color:rgb(105, 112, 152);font-style:italic">/* TEMPERATURE_TEMPERATURE_PORT_H */</span><br></div></code></pre></div></div>
<p>Внутрішній заголовок містить дані та функції, які потрібні файлам модуля спільно і яких ніхто ззовні ніколи не повинен бачити:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Temperature.h</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \file Temperature.h</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \ingroup Temperature</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \brief Temperature module internal definitions, not visible outside of the module</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifndef</span><span class="token macro property"> </span><span class="token macro property expression">TEMPERATURE_TEMPERATURE_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">TEMPERATURE_TEMPERATURE_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ============================== INCLUDES ================================== */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdbool.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Temperature_Types.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ============================== TYPEDEFS ================================== */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">struct</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">temperature_Value_t</span><span class="token plain"> filteredValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    bool                isValid</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token class-name" style="color:rgb(255, 203, 107)">temperature_State_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ========================= EXPORTED FUNCTIONS ============================= */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* Shared by the files of this module only */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token class-name" style="color:rgb(255, 203, 107)">temperature_Value_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Temperature_Filter_Apply</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">temperature_Value_t</span><span class="token plain"> rawValue </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><span class="token macro property"> </span><span class="token macro property comment" style="color:rgb(105, 112, 152);font-style:italic">/* TEMPERATURE_TEMPERATURE_H */</span><br></div></code></pre></div></div>
<p>А реалізація використовує все це:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Temperature.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Temperature.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Temperature_Port.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">temperature_State_t</span><span class="token plain"> state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Temperature_Init</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">filteredValue </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">isValid       </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> false</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Temperature_Task</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">temperature_Value_t</span><span class="token plain"> rawValue </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">253</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* a real module reads it from the ADC */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">filteredValue </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Temperature_Filter_Apply</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">rawValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">isValid       </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> true</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token class-name" style="color:rgb(255, 203, 107)">temperature_RequestState_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Temperature_Get_Value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">temperature_Value_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> value </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">temperature_RequestState_t</span><span class="token plain"> requestState </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> TEMPERATURE_REQUEST_ERROR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">isValid</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">value </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> state</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">filteredValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        requestState </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> TEMPERATURE_REQUEST_OK</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> requestState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Зверніть увагу, що стан є <code>static</code> у <code>.c</code>-файлі. Глобальної змінної немає, отже, немає й <code>extern</code> (який мій стиль кодування забороняє). Єдиний шлях до даних — функція <code>Temperature_Get_Value()</code>, геттер, який також повідомляє викликачу, чи дійсне значення.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="система-збірки-робить-файл-інкапсуляцією">Система збірки робить файл інкапсуляцією<a href="https://embedbits.cz/uk/blog/file-organization-embedded-c#%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0-%D0%B7%D0%B1%D1%96%D1%80%D0%BA%D0%B8-%D1%80%D0%BE%D0%B1%D0%B8%D1%82%D1%8C-%D1%84%D0%B0%D0%B9%D0%BB-%D1%96%D0%BD%D0%BA%D0%B0%D0%BF%D1%81%D1%83%D0%BB%D1%8F%D1%86%D1%96%D1%94%D1%8E" class="hash-link" aria-label="Пряме посилання на Система збірки робить файл інкапсуляцією" title="Пряме посилання на Система збірки робить файл інкапсуляцією" translate="no">​</a></h3>
<p>«Підключайте лише порт» було б просто побажанням, якби цього ніщо не забезпечувало. Забезпечує <code>CMakeLists.txt</code> кожного модуля. Він збирає статичну бібліотеку і <strong>копіює лише публічні заголовки</strong> в окрему теку в каталозі збірки. Лише ця тека потрапляє в include path користувачів бібліотеки:</p>
<div class="language-cmake codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">CMakeLists.txt</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cmake codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># Source files list for current library generation</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> Temperature_SourceFiles</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    Temperature.c</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    Temperature_Filter.c</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># List of public header files provided by the current library</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> Temperature_PublicHeaders</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    Temperature_Types.h</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    Temperature_Port.h</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># Create library</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">add_library</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">Temperature_Lib </span><span class="token namespace" style="color:rgb(178, 204, 214)">STATIC</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">Temperature_SourceFiles</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># Set public headers directory path in build folder</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">PUBLIC_HEADERS_DIR </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_CURRENT_BINARY_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/PublicHeaders</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">file</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">MAKE_DIRECTORY </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">PUBLIC_HEADERS_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># Copy only the public headers there</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">foreach</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">PublicHeader IN LISTS Temperature_PublicHeaders</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">configure_file</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_CURRENT_SOURCE_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/</span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">PublicHeader</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">PUBLIC_HEADERS_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">/</span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">PublicHeader</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        COPYONLY</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">endforeach</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"># Set include directories for the created target</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">target_include_directories</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> Temperature_Lib</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token namespace" style="color:rgb(178, 204, 214)">PUBLIC</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">$&lt;</span><span class="token plain">BUILD_INTERFACE:</span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token plain">PUBLIC_HEADERS_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token namespace" style="color:rgb(178, 204, 214)">PRIVATE</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">${</span><span class="token variable" style="color:rgb(191, 199, 213)">CMAKE_CURRENT_SOURCE_DIR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><br></div></code></pre></div></div>
<p>(Це спрощена версія шаблону, який генерує платформа, що також опрацьовує залежності між бібліотеками та шляхи Doxygen.) Тепер застосунок лише лінкує <code>Temperature_Lib</code>:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">AppMain.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdio.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Temperature_Port.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">int</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">main</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">temperature_Value_t</span><span class="token plain"> value </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Temperature_Init</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Temperature_Task</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">TEMPERATURE_REQUEST_OK </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Temperature_Get_Value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token function" style="color:rgb(130, 170, 255)">printf</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"%d\n"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Я спробував, що буде, якщо застосунок підключить внутрішній заголовок модуля, і компілятор каже те, що ви від нього хочете почути:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">AppMain_Cheat.c:1:10: fatal error: Temperature.h: No such file or directory</span><br></div></code></pre></div></div>
<p>Тека з публічними заголовками в каталозі збірки містить рівно два файли: <code>Temperature_Port.h</code> і <code>Temperature_Types.h</code>.</p>
<p><strong>Чесне застереження:</strong> це захист системи збірки, а не мови. Якщо хтось напише <code>#include "../../Middlewares/Temperature/Temperature.h"</code>, воно скомпілюється без жодних скарг (це я теж пробував). Лік дешевий: відхиляйте в CI відносні шляхи з <code>..</code> в include.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token function" style="color:rgb(130, 170, 255)">grep</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-rEn</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'#include "[^"]*\.\./'</span><span class="token plain"> Application Middlewares/*/ </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;&amp;</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">exit</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="коли-модуль-росте-компоненти">Коли модуль росте: компоненти<a href="https://embedbits.cz/uk/blog/file-organization-embedded-c#%D0%BA%D0%BE%D0%BB%D0%B8-%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C-%D1%80%D0%BE%D1%81%D1%82%D0%B5-%D0%BA%D0%BE%D0%BC%D0%BF%D0%BE%D0%BD%D0%B5%D0%BD%D1%82%D0%B8" class="hash-link" aria-label="Пряме посилання на Коли модуль росте: компоненти" title="Пряме посилання на Коли модуль росте: компоненти" translate="no">​</a></h3>
<p>Модуль з тисячею рядків в одному файлі — наступна проблема. Відповідь — <strong>компонент</strong>: ще одна пара файлів <code>Module_Component.c/.h</code> у тій самій теці. У прикладі вище це фільтр:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Temperature_Filter.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Temperature.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">temperature_Value_t</span><span class="token plain"> lastValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token class-name" style="color:rgb(255, 203, 107)">temperature_Value_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Temperature_Filter_Apply</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">temperature_Value_t</span><span class="token plain"> rawValue </span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    lastValue </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">temperature_Value_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">lastValue </span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain"> rawValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">/</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">2</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> lastValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Заголовок компонента також внутрішній (його немає у списку публічних заголовків), тож компонент можна замінити або розділити так, що ніхто цього не помітить. Користувач модуля й надалі бачить ті самі два файли. Правило, що ім'я файлу починається з імені модуля, робить компонент помітним у будь-якому списку файлів, а ім'я функції <code>Temperature_Filter_Apply()</code> підказує, де її шукати.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="рівень-3-усередині-файлу">Рівень 3: усередині файлу<a href="https://embedbits.cz/uk/blog/file-organization-embedded-c#%D1%80%D1%96%D0%B2%D0%B5%D0%BD%D1%8C-3-%D1%83%D1%81%D0%B5%D1%80%D0%B5%D0%B4%D0%B8%D0%BD%D1%96-%D1%84%D0%B0%D0%B9%D0%BB%D1%83" class="hash-link" aria-label="Пряме посилання на Рівень 3: усередині файлу" title="Пряме посилання на Рівень 3: усередині файлу" translate="no">​</a></h2>
<p>Кожен <code>.c</code>- і <code>.h</code>-файл, згенерований платформою, має однаковий каркас із банером для кожної секції в однаковому порядку:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/**</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \author ...</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \file Temperature.c</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \ingroup Temperature</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * \brief ...</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ============================== INCLUDES ================================== */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ============================== TYPEDEFS ================================== */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ======================== FORWARD DECLARATIONS ============================ */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ========================== SYMBOLIC CONSTANTS ============================ */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* =============================== MACROS =================================== */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ========================== EXPORTED VARIABLES ============================ */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* =========================== LOCAL VARIABLES ============================== */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ========================= EXPORTED FUNCTIONS ============================= */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* =========================== LOCAL FUNCTIONS ============================== */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* =========================== INTERRUPT HANDLERS =========================== */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ================================ TASKS =================================== */</span><br></div></code></pre></div></div>
<p>Це схоже на прикрасу, а насправді — карта. Коли ви відкриваєте файл, якого ніколи не бачили, ви знаєте, що локальні змінні в п'ятій секції, а обробники переривань — у десятій, і вам не потрібно читати файл, щоб їх знайти. Заголовки мають ту саму ідею, загорнуту в include guard і в <code>extern "C"</code> для користувачів C++:</p>
<ul>
<li class="">guard має вигляд <code>MODULE_FILE_H</code>, наприклад <code>TEMPERATURE_TEMPERATURE_PORT_H</code>,</li>
<li class="">заголовок Doxygen (<code>\file</code>, <code>\ingroup</code>, <code>\brief</code>) є на початку кожного файлу, тож документація модуля генерується з тієї самої теки,</li>
<li class="">підключення впорядковані так: <code>&lt;system&gt;</code>, потім проєкт, потім модуль.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="одне-імя-скрізь">Одне ім'я скрізь<a href="https://embedbits.cz/uk/blog/file-organization-embedded-c#%D0%BE%D0%B4%D0%BD%D0%B5-%D1%96%D0%BC%D1%8F-%D1%81%D0%BA%D1%80%D1%96%D0%B7%D1%8C" class="hash-link" aria-label="Пряме посилання на Одне ім'я скрізь" title="Пряме посилання на Одне ім'я скрізь" translate="no">​</a></h2>
<p>Сила цієї організації в тому, що ім'я модуля скрізь однакове, тож ім'я можна вгадати, не шукаючи його:</p>





































<table><thead><tr><th>Що</th><th>Приклад</th></tr></thead><tbody><tr><td>Тека</td><td><code>Temperature/</code></td></tr><tr><td>Бібліотека в CMake</td><td><code>Temperature_Lib</code></td></tr><tr><td>Файли</td><td><code>Temperature.c</code>, <code>Temperature_Port.h</code>, <code>Temperature_Types.h</code></td></tr><tr><td>Функції</td><td><code>Temperature_Init()</code>, <code>Temperature_Get_Value()</code></td></tr><tr><td>Типи</td><td><code>temperature_Value_t</code>, <code>temperature_RequestState_t</code></td></tr><tr><td>Макроси та перелічувані значення</td><td><code>TEMPERATURE_REQUEST_OK</code></td></tr><tr><td>Include guard</td><td><code>TEMPERATURE_TEMPERATURE_PORT_H</code></td></tr></tbody></table>
<p>Якщо ви бачите <code>Gpio_Set_PinLevel()</code> у стеку викликів, ви знаєте, що вона в теці <code>Gpio</code>, у бібліотеці <code>Gpio_Lib</code>, а її типи починаються з <code>gpio_</code>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="один-життєвий-цикл-для-кожного-модуля">Один життєвий цикл для кожного модуля<a href="https://embedbits.cz/uk/blog/file-organization-embedded-c#%D0%BE%D0%B4%D0%B8%D0%BD-%D0%B6%D0%B8%D1%82%D1%82%D1%94%D0%B2%D0%B8%D0%B9-%D1%86%D0%B8%D0%BA%D0%BB-%D0%B4%D0%BB%D1%8F-%D0%BA%D0%BE%D0%B6%D0%BD%D0%BE%D0%B3%D0%BE-%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8F" class="hash-link" aria-label="Пряме посилання на Один життєвий цикл для кожного модуля" title="Пряме посилання на Один життєвий цикл для кожного модуля" translate="no">​</a></h3>
<p>Згенерований модуль постачається з чотирма функціями, які має кожен модуль: <code>Get_ModuleVersion()</code>, <code>Init()</code>, <code>Deinit()</code> і <code>Task()</code>. <code>Init</code> налаштовує модуль і обробляє власні збої, <code>Task</code> викликається періодично з основного циклу або з планувальника. Наслідок у тому, що код нагорі (<code>AppMain</code>) може поводитися з усіма модулями однаково, а новому модулю не потрібна нова концепція.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="створення-модуля">Створення модуля<a href="https://embedbits.cz/uk/blog/file-organization-embedded-c#%D1%81%D1%82%D0%B2%D0%BE%D1%80%D0%B5%D0%BD%D0%BD%D1%8F-%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8F" class="hash-link" aria-label="Пряме посилання на Створення модуля" title="Пряме посилання на Створення модуля" translate="no">​</a></h2>
<p>Ці файли не створюють вручну. Інструменти проєкту платформи (пункт меню <em>Create module</em>, описаний на сторінці <a class="" href="https://embedbits.cz/uk/docs/platform/embi-platform/cmake-helpertools">Project tools</a>) запитують назву та розташування й генерують теку з усіма файлами вище та <code>CMakeLists.txt</code>. Пункт <em>Add component</em> додає пару <code>Module_Component.c/.h</code> до наявного модуля. Модуль, що був згенерований раз, ніколи не перезаписується, тож інструмент можна безпечно запускати знову.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="правила-коротко">Правила коротко<a href="https://embedbits.cz/uk/blog/file-organization-embedded-c#%D0%BF%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%B0-%D0%BA%D0%BE%D1%80%D0%BE%D1%82%D0%BA%D0%BE" class="hash-link" aria-label="Пряме посилання на Правила коротко" title="Пряме посилання на Правила коротко" translate="no">​</a></h2>
<ol>
<li class="">Один модуль — одна тека, і в нього є власна бібліотека.</li>
<li class="">Інші модулі підключають <strong>лише</strong> <code>Module_Port.h</code> і <code>Module_Types.h</code>.</li>
<li class="">Внутрішніх заголовків немає у списку публічних заголовків.</li>
<li class="">Дані — <code>static</code> у <code>.c</code>-файлі, доступ — через функції. Ніяких <code>extern</code>-змінних.</li>
<li class="">Ім'я файлу починається з імені модуля, так само як і кожна функція, тип і макрос.</li>
<li class="">Код вендора ніколи не редагується. Ваші зміни живуть у теці обробника поруч із ним.</li>
<li class="">Виклики в шарах ідуть вниз, ніколи вгору.</li>
<li class="">Усе у файлі розташоване у своїй секції, в однаковому порядку.</li>
</ol>
<p>Жодне з цих правил не хитре. Суть у тому, що всі вони однакові в усіх модулях, тож прочитавши один модуль, ви знаєте, як читати всі інші. Це також причина, чому платформа генерує файли: конвенція, дотримання якої потребує зусиль, — це конвенція, яку не дотримуються.</p>
<p>Ті самі ідеї з іншої точки зору є у статтях про <a class="" href="https://embedbits.cz/uk/blog/solid-principles-c-cpp">принципи SOLID</a> (заголовок порту — це інтерфейс, від якого залежать інші модулі) і про <a class="" href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice">MISRA C</a> (кілька правил значно легше дотримуватися, коли код організований так).</p>]]></content:encoded>
            <category>Вбудовані системи</category>
            <category>Архітектура</category>
            <category>C</category>
        </item>
        <item>
            <title><![CDATA[Правила MISRA C на практиці: що це таке, навіщо вони існують і як з ними жити]]></title>
            <link>https://embedbits.cz/uk/blog/misra-c-rules-in-practice</link>
            <guid>https://embedbits.cz/uk/blog/misra-c-rules-in-practice</guid>
            <pubDate>Sun, 04 Oct 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[Скажіть «MISRA» у кімнаті, повній embedded-розробників, і половина зітхне, а друга половина запитає, яким інструментом ви користуєтеся. Репутація цих правил неоднозначна: для одних це бюрократія, що з'їдає тиждень перед кожним релізом, для інших — причина, чому автомобіль не перезавантажується посеред траси. Обидві групи частково мають рацію.]]></description>
            <content:encoded><![CDATA[<p>Скажіть «MISRA» у кімнаті, повній embedded-розробників, і половина зітхне, а друга половина запитає, яким інструментом ви користуєтеся. Репутація цих правил неоднозначна: для одних це бюрократія, що з'їдає тиждень перед кожним релізом, для інших — причина, чому автомобіль не перезавантажується посеред траси. Обидві групи частково мають рацію.</p>
<p>У цій статті я пройдуся по правилах, які трапляються мені найчастіше, одне за одним: чого вимагає правило, що ви за це отримуєте, як виглядає поганий код, як виглядає добрий і хто знаходить проблему за вас. Усі приклади — це справжній код на C, який я скомпілював і запустив.</p>
<!-- -->
<blockquote>
<p><strong>Кілька слів про тексти правил</strong></p>
<p>MISRA C — документ, захищений авторським правом консорціуму MISRA. Я не копіюю його формулювань. Кожне правило описано моїми словами, з моїми прикладами, а цитуються лише номери та категорії. Номери стосуються <strong>MISRA C:2012</strong> (MISRA C:2023 об'єднує поправки, зберігає ці номери й додає кілька нових настанов). Перш ніж будувати щось на основі цієї статті, перевірте правило у власній копії стандарту, яку можна отримати на <a href="https://misra.org.uk/" target="_blank" rel="noopener noreferrer" class="">misra.org.uk</a>. Ця стаття незалежна і не схвалена MISRA.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="що-таке-misra-c-і-чим-вона-не-є">Що таке MISRA C (і чим вона не є)<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#%D1%89%D0%BE-%D1%82%D0%B0%D0%BA%D0%B5-misra-c-%D1%96-%D1%87%D0%B8%D0%BC-%D0%B2%D0%BE%D0%BD%D0%B0-%D0%BD%D0%B5-%D1%94" class="hash-link" aria-label="Пряме посилання на Що таке MISRA C (і чим вона не є)" title="Пряме посилання на Що таке MISRA C (і чим вона не є)" translate="no">​</a></h2>
<p>MISRA розшифровується як <em>Motor Industry Software Reliability Association</em>. Перші настанови для C було опубліковано у 1998 році для автомобільної галузі, сьогодні їх використовують в авіакосмічній галузі, на залізниці, у медичних пристроях та промисловій автоматизації — по суті, скрізь, де збій програмного забезпечення комусь шкодить.</p>
<p>Центральна ідея проста. C — мова, яка дозволяє багато речей, що є легальними, але небезпечними: undefined behaviour, implementation defined behaviour, неявні перетворення, що змінюють значення, вказівники, які можуть указувати будь-куди. MISRA визначає <strong>підмножину C</strong>, у якій небезпечні частини заборонені або мають бути обґрунтовані. Це не інша мова, і програма, сумісна з MISRA, залишається звичайною програмою на C, яку збирає ваш звичайний компілятор.</p>
<p>Чим MISRA <strong>не є</strong>:</p>
<ul>
<li class="">Це не сертифікація. «Сумісність з MISRA» — це твердження, яке ви робите і документуєте, ніхто не ставить печатку на ваш код.</li>
<li class="">Це не гарантія коректності. Правила усувають класи помилок, але не перевіряють, що термостат справді регулює температуру.</li>
<li class="">Це не про стиль. Іменування, відступи та організація файлів не входять до MISRA, для цього існує <a class="" href="https://embedbits.cz/uk/docs/platform/embi-platform/coding-style">coding style</a>.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="як-читати-правило">Як читати правило<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#%D1%8F%D0%BA-%D1%87%D0%B8%D1%82%D0%B0%D1%82%D0%B8-%D0%BF%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%BE" class="hash-link" aria-label="Пряме посилання на Як читати правило" title="Пряме посилання на Як читати правило" translate="no">​</a></h3>
<p>Кожна настанова має три властивості, які варто знати, перш ніж читати першу з них:</p>

























<table><thead><tr><th>Властивість</th><th>Значення</th><th>Що означає</th></tr></thead><tbody><tr><td><strong>Вид</strong></td><td><em>Directive</em> / <em>Rule</em></td><td>Правило (rule) можна перевірити, дивлячись лише на вихідний код. Директива (directive) потребує додаткової інформації (дизайну, процесу), тож інструмент може допомогти, але не може вирішити.</td></tr><tr><td><strong>Категорія</strong></td><td><em>Mandatory</em> / <em>Required</em> / <em>Advisory</em></td><td>Mandatory: жодних відхилень. Required: відхилення можливе за наявності письмового обґрунтування. Advisory: рекомендація, потрібно лише зафіксувати, що ви її не дотримуєтеся.</td></tr><tr><td><strong>Вирішуваність</strong></td><td><em>Decidable</em> / <em>Undecidable</em></td><td>Чи може інструмент дати точну відповідь. Для невирішуваних правил навіть найкращий аналізатор видає хибні спрацьовування або щось пропускає.</td></tr></tbody></table>
<p>Нумерація має вигляд <code>Dir 4.12</code> для директив і <code>Rule 9.1</code> для правил, де перше число — це тема (9 = ініціалізація, 10 = типи, 11 = вказівники, ...).</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="правила-одне-за-одним">Правила одне за одним<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#%D0%BF%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%B0-%D0%BE%D0%B4%D0%BD%D0%B5-%D0%B7%D0%B0-%D0%BE%D0%B4%D0%BD%D0%B8%D0%BC" class="hash-link" aria-label="Пряме посилання на Правила одне за одним" title="Пряме посилання на Правила одне за одним" translate="no">​</a></h2>
<p>Я згрупував їх за проблемою, яку вони вирішують, а не за номером.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="правила-про-значення-які-не-такі-як-ви-думаєте">Правила про значення, які не такі, як ви думаєте<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#%D0%BF%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%B0-%D0%BF%D1%80%D0%BE-%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%BD%D1%8F-%D1%8F%D0%BA%D1%96-%D0%BD%D0%B5-%D1%82%D0%B0%D0%BA%D1%96-%D1%8F%D0%BA-%D0%B2%D0%B8-%D0%B4%D1%83%D0%BC%D0%B0%D1%94%D1%82%D0%B5" class="hash-link" aria-label="Пряме посилання на Правила про значення, які не такі, як ви думаєте" title="Пряме посилання на Правила про значення, які не такі, як ви думаєте" translate="no">​</a></h3>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="rule-91---читання-перед-записом-mandatory">Rule 9.1 - читання перед записом (Mandatory)<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#rule-91---%D1%87%D0%B8%D1%82%D0%B0%D0%BD%D0%BD%D1%8F-%D0%BF%D0%B5%D1%80%D0%B5%D0%B4-%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D0%BE%D0%BC-mandatory" class="hash-link" aria-label="Пряме посилання на Rule 9.1 - читання перед записом (Mandatory)" title="Пряме посилання на Rule 9.1 - читання перед записом (Mandatory)" translate="no">​</a></h4>
<p><strong>Правило:</strong> не читайте змінну з автоматичним часом зберігання, доки їй не було присвоєно значення.</p>
<p><strong>Що ви отримуєте:</strong> значення такої змінної — сміття, а на іншому компіляторі чи рівні оптимізації це <em>інше</em> сміття. Помилки на кшталт «у debug працює, у release ламається» дуже часто закінчуються саме тут. Оскільки правило mandatory, жодне відхилення неможливе.</p>
<p>Погано, змінна встановлюється лише на одному шляху:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Gain_Bad</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">bool isHigh</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> gain</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">isHigh</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> gain </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">8u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> gain</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">                       </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* indeterminate value when isHigh is false */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Добре, кожен шлях має визначене значення:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Gain_Good</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">bool isHigh</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> gain </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">isHigh</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> gain </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">8u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> gain</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p><strong>Хто це знаходить:</strong> компілятор — іноді (<code>-Wall -Wmaybe-uninitialized</code> з оптимізацією), статичний аналізатор — надійно. У моїй тестовій збірці gcc промовчав на цій функції. Не покладайтеся на нього.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="rules-101-103-104---essential-types-required">Rules 10.1, 10.3, 10.4 - essential types (Required)<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#rules-101-103-104---essential-types-required" class="hash-link" aria-label="Пряме посилання на Rules 10.1, 10.3, 10.4 - essential types (Required)" title="Пряме посилання на Rules 10.1, 10.3, 10.4 - essential types (Required)" translate="no">​</a></h4>
<p><strong>Правила:</strong> автори MISRA вигадали <em>модель essential type</em>: тип, який вираз <em>виглядає так, ніби має</em> (<code>uint8_t</code> лишається в їхньому розумінні «беззнаковим 8-бітним»), на противагу типу, який C мовчки використовує (<code>int</code>, через integer promotion). Правила забороняють операції між невідповідними типами, присвоєння значення вужчому типу та змішування категорій (знакові, беззнакові, булеві, символьні, з рухомою комою).</p>
<p><strong>Що ви отримуєте:</strong> integer promotion — найбільше джерело сюрпризів в embedded C. Усі значення, менші за <code>int</code>, перед будь-якою арифметикою розширюються до <code>int</code>, і ви цього не бачите. Два класичні результати:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">bool </span><span class="token function" style="color:rgb(130, 170, 255)">Flags_Bad</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> flags</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">~</span><span class="token plain">flags </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0xF0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">          </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ~flags is an int: 0xFFFFFFF0 - never equal */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p><code>~flags</code> — це не 8-бітне значення, а <code>int</code> зі значенням <code>0xFFFFFFF0</code>. Порівняння ніколи не спрацьовує, хоч би який був аргумент. Виправлення — сказати, що саме ви маєте на увазі:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">bool </span><span class="token function" style="color:rgb(130, 170, 255)">Flags_Good</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> flags</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token operator" style="color:rgb(137, 221, 255)">~</span><span class="token plain">flags </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0xF0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Другий класичний випадок — мовчазне звуження:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Add_Bad</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> first</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> second</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> sum </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> first </span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain"> second</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">      </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* int result silently narrowed */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> sum</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p><code>200 + 100</code> обчислюється як <code>int</code> (300), а присвоєння в <code>uint8_t</code> залишає лише 44. Ні попередження, ні помилки, а сума неправильна. Два коректні варіанти: розширити результат або виконати свідоме приведення типу й подумати про переповнення.</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token class-name" style="color:rgb(255, 203, 107)">uint16_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Add_Good</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> first</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> second</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint16_t</span><span class="token plain"> sum </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint16_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">first </span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint16_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">second</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> sum</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p><strong>Хто це знаходить:</strong> хороший статичний аналізатор. Компілятор допомагає лише частково: <code>-Wsign-compare</code> (частина <code>-Wextra</code>) повідомив про перший приклад, а на другий gcc промовчав.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="directive-46---типи-з-розміром-і-знаковістю-advisory">Directive 4.6 - типи з розміром і знаковістю (Advisory)<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#directive-46---%D1%82%D0%B8%D0%BF%D0%B8-%D0%B7-%D1%80%D0%BE%D0%B7%D0%BC%D1%96%D1%80%D0%BE%D0%BC-%D1%96-%D0%B7%D0%BD%D0%B0%D0%BA%D0%BE%D0%B2%D1%96%D1%81%D1%82%D1%8E-advisory" class="hash-link" aria-label="Пряме посилання на Directive 4.6 - типи з розміром і знаковістю (Advisory)" title="Пряме посилання на Directive 4.6 - типи з розміром і знаковістю (Advisory)" translate="no">​</a></h4>
<p><strong>Директива:</strong> використовуйте typedef, що вказують розмір і знаковість (<code>uint8_t</code>, <code>int32_t</code> або ваші власні назви), замість <code>char</code>, <code>short</code>, <code>int</code> і <code>long</code>.</p>
<p><strong>Що ви отримуєте:</strong> ширина <code>int</code> залежить від компілятора і MCU: 16 біт на малих 8- і 16-бітних контролерах, 32 біти на Cortex-M. Код, що працює на одній цільовій платформі, мовчки змінюється на іншій. З типами фіксованої ширини в коді видно, що він робить. У своїх проєктах я йду на крок далі й створюю власні назви типів для фізичних величин (<code>temperature_t</code>, <code>humidity_t</code>), щоб компілятор зупиняв мене, коли я їх змішую.</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">int</span><span class="token plain"> counter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">       </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* 16 or 32 bits? the answer depends on the target */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token class-name" style="color:rgb(255, 203, 107)">uint16_t</span><span class="token plain"> counter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">  </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* 16 bits everywhere */</span><br></div></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="rule-13---жодної-undefined-behaviour-required">Rule 1.3 - жодної undefined behaviour (Required)<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#rule-13---%D0%B6%D0%BE%D0%B4%D0%BD%D0%BE%D1%97-undefined-behaviour-required" class="hash-link" aria-label="Пряме посилання на Rule 1.3 - жодної undefined behaviour (Required)" title="Пряме посилання на Rule 1.3 - жодної undefined behaviour (Required)" translate="no">​</a></h4>
<p><strong>Правило:</strong> програма не повинна містити undefined behaviour. Сюди входять переповнення знакових чисел, зсуви на надто велику кількість біт, ділення на нуль, використання «висячого» вказівника та багато іншого.</p>
<p><strong>Що ви отримуєте:</strong> за undefined behaviour компілятор може зробити <em>що завгодно</em>, зокрема прибрати вашу захисну перевірку, бо «цього випадку не може бути». Найвідоміший приклад — переповнення знакових чисел: <code>first + second</code> для <code>int32_t</code>, що не вміщується, — це не «перехід через нуль», а ліцензія для оптимізатора.</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token class-name" style="color:rgb(255, 203, 107)">int32_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Add_Bad2</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">int32_t</span><span class="token plain"> first</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">int32_t</span><span class="token plain"> second</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> first </span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain"> second</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">             </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* signed overflow is undefined behaviour */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Коректний код перевіряє умову <em>перед</em> операцією і повідомляє про проблему викликачеві:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">bool </span><span class="token function" style="color:rgb(130, 170, 255)">Add_Safe</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">int32_t</span><span class="token plain"> first</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">int32_t</span><span class="token plain"> second</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">int32_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">result</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    bool isOk </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> true</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">second </span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;&amp;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">first </span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">INT32_MAX </span><span class="token operator" style="color:rgb(137, 221, 255)">-</span><span class="token plain"> second</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">||</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">second </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;&amp;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">first </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">INT32_MIN </span><span class="token operator" style="color:rgb(137, 221, 255)">-</span><span class="token plain"> second</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        isOk </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> false</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">else</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">result </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> first </span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain"> second</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> isOk</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Те саме стосується зсувів. Маска позиції біта визначена лише тоді, коли позиція менша за ширину типу:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Bit_Mask</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> position</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">position </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">32u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">?</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">1u</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;&lt;</span><span class="token plain"> position</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p><strong>Хто це знаходить:</strong> частково компілятор (<code>-fsanitize=undefined</code> знаходить це під час виконання в тестах), частково аналізатор. Загалом правило невирішуване: інструмент може показати всі місця, які могли б бути проблемою.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="правила-про-умови-та-потік-керування">Правила про умови та потік керування<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#%D0%BF%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%B0-%D0%BF%D1%80%D0%BE-%D1%83%D0%BC%D0%BE%D0%B2%D0%B8-%D1%82%D0%B0-%D0%BF%D0%BE%D1%82%D1%96%D0%BA-%D0%BA%D0%B5%D1%80%D1%83%D0%B2%D0%B0%D0%BD%D0%BD%D1%8F" class="hash-link" aria-label="Пряме посилання на Правила про умови та потік керування" title="Пряме посилання на Правила про умови та потік керування" translate="no">​</a></h3>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="rule-144---керівний-вираз-є-булевим-required">Rule 14.4 - керівний вираз є булевим (Required)<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#rule-144---%D0%BA%D0%B5%D1%80%D1%96%D0%B2%D0%BD%D0%B8%D0%B9-%D0%B2%D0%B8%D1%80%D0%B0%D0%B7-%D1%94-%D0%B1%D1%83%D0%BB%D0%B5%D0%B2%D0%B8%D0%BC-required" class="hash-link" aria-label="Пряме посилання на Rule 14.4 - керівний вираз є булевим (Required)" title="Пряме посилання на Rule 14.4 - керівний вираз є булевим (Required)" translate="no">​</a></h4>
<p><strong>Правило:</strong> умова в <code>if</code>, <code>while</code> і <code>for</code> має бути виразом з essential Boolean типом, тобто результатом порівняння або <code>bool</code>. Одне ціле число чи вказівник не дозволені.</p>
<p><strong>Що ви отримуєте:</strong> намір читається. <code>while (count)</code> не говорить, чи ви хочете «поки не нуль», чи «поки не кінець». Явна форма виглядає однаково для чисел, вказівників і прапорців. (Константу я пишу ліворуч, <code>0u != count</code>, щоб помилка з одним <code>=</code> не компілювалася.)</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Count_Bad</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> count</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> steps </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">while</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">count</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> count</span><span class="token operator" style="color:rgb(137, 221, 255)">--</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> steps</span><span class="token operator" style="color:rgb(137, 221, 255)">++</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> steps</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Count_Good</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> count</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> steps </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">while</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">!=</span><span class="token plain"> count</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> count</span><span class="token operator" style="color:rgb(137, 221, 255)">--</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> steps</span><span class="token operator" style="color:rgb(137, 221, 255)">++</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> steps</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p><strong>Хто це знаходить:</strong> кожен аналізатор MISRA, це вирішуване правило.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="rule-121---робіть-пріоритет-операторів-явним-advisory">Rule 12.1 - робіть пріоритет операторів явним (Advisory)<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#rule-121---%D1%80%D0%BE%D0%B1%D1%96%D1%82%D1%8C-%D0%BF%D1%80%D1%96%D0%BE%D1%80%D0%B8%D1%82%D0%B5%D1%82-%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D0%BE%D1%80%D1%96%D0%B2-%D1%8F%D0%B2%D0%BD%D0%B8%D0%BC-advisory" class="hash-link" aria-label="Пряме посилання на Rule 12.1 - робіть пріоритет операторів явним (Advisory)" title="Пряме посилання на Rule 12.1 - робіть пріоритет операторів явним (Advisory)" translate="no">​</a></h4>
<p><strong>Правило:</strong> не розраховуйте, що читач (і ви самі) пам'ятатимете таблицю пріоритетів, — використовуйте дужки.</p>
<p><strong>Що ви отримуєте:</strong> пріоритет <code>&amp;</code> та <code>==</code> у C протилежний тому, чого очікує людський мозок. Це помилка, яку кожен робить принаймні раз:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">bool </span><span class="token function" style="color:rgb(130, 170, 255)">Masked_Bad</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> flags</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">flags </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain"> MASK </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> MASK</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">     </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* == binds stronger than &amp; */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Спочатку обчислюється <code>MASK == MASK</code> і дає <code>1</code>, тож функція перевіряє біт 0, а не маску. Вона повертає <code>true</code> для <code>0x01</code> і <code>false</code> для <code>0x0C</code> — рівно навпаки. З дужками ця помилка не може існувати:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">bool </span><span class="token function" style="color:rgb(130, 170, 255)">Masked_Good</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> flags</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">flags </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain"> MASK</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> MASK</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p><strong>Хто це знаходить:</strong> компілятор (<code>-Wparentheses</code> входить до <code>-Wall</code>) та кожен аналізатор.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="rules-156-157-163-і-164---завершені-інструкції-required">Rules 15.6, 15.7, 16.3 і 16.4 - завершені інструкції (Required)<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#rules-156-157-163-%D1%96-164---%D0%B7%D0%B0%D0%B2%D0%B5%D1%80%D1%88%D0%B5%D0%BD%D1%96-%D1%96%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%86%D1%96%D1%97-required" class="hash-link" aria-label="Пряме посилання на Rules 15.6, 15.7, 16.3 і 16.4 - завершені інструкції (Required)" title="Пряме посилання на Rules 15.6, 15.7, 16.3 і 16.4 - завершені інструкції (Required)" translate="no">​</a></h4>
<p><strong>Правила:</strong></p>
<ul>
<li class="">15.6: тіло <code>if</code>, <code>else</code>, <code>for</code>, <code>while</code> — це завжди блок у фігурних дужках,</li>
<li class="">15.7: кожен ланцюжок <code>if ... else if</code> закінчується <code>else</code>,</li>
<li class="">16.3: кожна гілка <code>switch</code> закінчується <code>break</code> (або іншим безумовним переходом),</li>
<li class="">16.4: кожен <code>switch</code> має <code>default</code>.</li>
</ul>
<p><strong>Що ви отримуєте:</strong> правила змушують відповісти на запитання «що відбувається в усіх інших випадках?» у момент написання коду, а не в польових умовах. <code>switch</code> без <code>break</code> — класична помилка, <code>enum</code>, який отримує нове значення без <code>default</code>, — друга. Фігурні дужки усувають сім'ю помилок, коли під <code>if</code> додають другу інструкцію, і вона виглядає так, ніби теж умовна:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">isError</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Led_On</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Buzzer_On</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">      </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* indented like it belongs to the if - it does not */</span><br></div></code></pre></div></div>
<p>А ось switch. Перша версія «провалюється» з <code>MODE_ON</code> у <code>MODE_ERROR</code> і встановлює неправильне значення, друга обробляє кожен випадок явно:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Mode_Bad</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">mode_t_ mode</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">switch</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">mode</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">case</span><span class="token plain"> MODE_ON</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">            gLeds </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">case</span><span class="token plain"> MODE_ERROR</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain">               </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* falls through by accident */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">            gLeds </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">3u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">            </span><span class="token keyword" style="font-style:italic">break</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Mode_Good</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">mode_t_ mode</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">switch</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">mode</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">case</span><span class="token plain"> MODE_ON</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">            gLeds </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">            </span><span class="token keyword" style="font-style:italic">break</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">case</span><span class="token plain"> MODE_ERROR</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">            gLeds </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">3u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">            </span><span class="token keyword" style="font-style:italic">break</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">case</span><span class="token plain"> MODE_OFF</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">default</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">            gLeds </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">            </span><span class="token keyword" style="font-style:italic">break</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>У моєму тесті gcc знайшов відсутній <code>default</code> і fall-through (<code>-Wswitch-default -Wimplicit-fallthrough</code>). Правило про <code>else</code> і фігурні дужки перевіряє аналізатор або форматер.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="rule-172---жодної-рекурсії-required">Rule 17.2 - жодної рекурсії (Required)<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#rule-172---%D0%B6%D0%BE%D0%B4%D0%BD%D0%BE%D1%97-%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D1%96%D1%97-required" class="hash-link" aria-label="Пряме посилання на Rule 17.2 - жодної рекурсії (Required)" title="Пряме посилання на Rule 17.2 - жодної рекурсії (Required)" translate="no">​</a></h4>
<p><strong>Правило:</strong> функція не повинна викликати саму себе — ні безпосередньо, ні через інші функції.</p>
<p><strong>Що ви отримуєте:</strong> на ПК стек має мегабайти. На мікроконтролері у вас кілька кілобайтів, а найгірший випадок глибини рекурсії залежить від вхідних даних. Якщо прибрати рекурсію, максимальне використання стека можна <em>обчислити</em> (достатньо статичного аналізу дерева викликів), і переповнення стека перестає бути сюрпризом.</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Factorial_Recursive</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">value </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">?</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1u</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> value </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Factorial_Recursive</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">value </span><span class="token operator" style="color:rgb(137, 221, 255)">-</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Ітеративна версія потребує сталого обсягу стека:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Factorial_Iterative</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> result </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">for</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> factor </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">2u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> factor </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;=</span><span class="token plain"> value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> factor</span><span class="token operator" style="color:rgb(137, 221, 255)">++</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> result </span><span class="token operator" style="color:rgb(137, 221, 255)">*=</span><span class="token plain"> factor</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> result</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p><strong>Хто це знаходить:</strong> аналізатор і linker map (деякі toolchain друкують граф викликів, <code>-fstack-usage</code> показує розмір кожного фрейма).</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="правила-про-память-і-вказівники">Правила про пам'ять і вказівники<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#%D0%BF%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%B0-%D0%BF%D1%80%D0%BE-%D0%BF%D0%B0%D0%BC%D1%8F%D1%82%D1%8C-%D1%96-%D0%B2%D0%BA%D0%B0%D0%B7%D1%96%D0%B2%D0%BD%D0%B8%D0%BA%D0%B8" class="hash-link" aria-label="Пряме посилання на Правила про пам'ять і вказівники" title="Пряме посилання на Правила про пам'ять і вказівники" translate="no">​</a></h3>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="directive-412-і-rule-213---жодної-динамічної-памяті-required">Directive 4.12 і Rule 21.3 - жодної динамічної пам'яті (Required)<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#directive-412-%D1%96-rule-213---%D0%B6%D0%BE%D0%B4%D0%BD%D0%BE%D1%97-%D0%B4%D0%B8%D0%BD%D0%B0%D0%BC%D1%96%D1%87%D0%BD%D0%BE%D1%97-%D0%BF%D0%B0%D0%BC%D1%8F%D1%82%D1%96-required" class="hash-link" aria-label="Пряме посилання на Directive 4.12 і Rule 21.3 - жодної динамічної пам'яті (Required)" title="Пряме посилання на Directive 4.12 і Rule 21.3 - жодної динамічної пам'яті (Required)" translate="no">​</a></h4>
<p><strong>Правило:</strong> не використовуйте <code>malloc</code>, <code>calloc</code>, <code>realloc</code> і <code>free</code>.</p>
<p><strong>Що ви отримуєте:</strong></p>
<ul>
<li class="">купа (heap) <em>фрагментується</em>. Після тижнів роботи запит на 64 байти може завершитися невдачею, хоча вільно 10 кБ, лише не суцільним шматком,</li>
<li class="">час виконання <code>malloc</code> не детермінований,</li>
<li class="">витік пам'яті після тижнів роботи — найгірший вид помилки для пошуку,</li>
<li class="">споживання пам'яті відоме під час лінкування, а не о 3-й ночі в полі.</li>
</ul>
<p>Стандартна альтернатива — пул фіксованого розміру, який має ту саму форму API, що й алокатор:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">FRAME_POOL_SIZE</span><span class="token macro property"> </span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">4u</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">struct</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token number" style="color:rgb(247, 140, 108)">8</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    bool    inUse</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token class-name" style="color:rgb(255, 203, 107)">frame_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">frame_t</span><span class="token plain"> framePool</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">FRAME_POOL_SIZE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token class-name" style="color:rgb(255, 203, 107)">frame_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token function" style="color:rgb(130, 170, 255)">Frame_Acquire</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">for</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> index </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> index </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain"> FRAME_POOL_SIZE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> index</span><span class="token operator" style="color:rgb(137, 221, 255)">++</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">!</span><span class="token plain">framePool</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">index</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">inUse</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> framePool</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">index</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">inUse </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> true</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">framePool</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">index</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token constant" style="color:rgb(130, 170, 255)">NULL</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">                       </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* the caller has to handle "no frame" - at test time, not at 3 a.m. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Функція повертає <code>NULL</code>, якщо всі фрейми зайняті, тож випадок «немає пам'яті» обробляється в коді й тестується на ПК. Точно те саме відбувається з <code>malloc</code>, лише ви не можете цього протестувати, бо на вашому ПК виділення пам'яті ніколи не завершується невдачею.</p>
<p><strong>Хто це знаходить:</strong> аналізатор або просто лінкер, який взагалі не підключає <code>malloc</code>.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="rules-113-і-115---обережно-з-приведенням-вказівників-required--advisory">Rules 11.3 і 11.5 - обережно з приведенням вказівників (Required / Advisory)<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#rules-113-%D1%96-115---%D0%BE%D0%B1%D0%B5%D1%80%D0%B5%D0%B6%D0%BD%D0%BE-%D0%B7-%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%BD%D1%8F%D0%BC-%D0%B2%D0%BA%D0%B0%D0%B7%D1%96%D0%B2%D0%BD%D0%B8%D0%BA%D1%96%D0%B2-required--advisory" class="hash-link" aria-label="Пряме посилання на Rules 11.3 і 11.5 - обережно з приведенням вказівників (Required / Advisory)" title="Пряме посилання на Rules 11.3 і 11.5 - обережно з приведенням вказівників (Required / Advisory)" translate="no">​</a></h4>
<p><strong>Правила:</strong> не приводьте вказівник на об'єкт одного типу до вказівника на об'єкт іншого типу (11.3) і уникайте перетворення з <code>void *</code> на вказівник на об'єкт (11.5).</p>
<p><strong>Що ви отримуєте:</strong> приведення байтового буфера до <code>uint32_t *</code> — найпоширеніший спосіб прочитати поле кадру зв'язку:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">ReadU32_Bad</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">buffer</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">buffer</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">  </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* misaligned access, strict aliasing violation */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Це undefined behaviour двічі. Адреса не зобов'язана бути вирівняною на чотири байти (hard fault на Cortex-M0, повільний доступ деінде), а доступ через вказівник іншого типу порушує правила aliasing, тож оптимізатор може переставити або викинути його. Два коректні рішення: <code>memcpy</code> (компілятор перетворює його на одне завантаження там, де це дозволяє апаратура) або складання з байтів, що заодно фіксує порядок байтів і не залежить від endianness процесора:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">ReadU32_Good</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">buffer</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token function" style="color:rgb(130, 170, 255)">memcpy</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> buffer</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">sizeof</span><span class="token plain"> value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">ReadU32LittleEndian</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">buffer</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">buffer</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">|</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">buffer</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;&lt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">8</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">|</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">buffer</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token number" style="color:rgb(247, 140, 108)">2</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;&lt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">16</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">|</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">buffer</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token number" style="color:rgb(247, 140, 108)">3</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;&lt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">24</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Приведення <code>void *</code> типове для загального контексту callback. Воно advisory, бо в C це іноді єдиний спосіб. Користь правила в тому, що ви помітите кожне таке місце і зробите приведення в одному рядку на початку функції (як у <a class="" href="https://embedbits.cz/uk/blog/solid-principles-c-cpp">статті про SOLID</a>), а не скрізь.</p>
<p><strong>Хто це знаходить:</strong> кожен аналізатор. Компілятор знаходить таке приведення лише на деяких цільових платформах і з <code>-Wcast-align</code>.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="rule-813---вказівник-на-const-коли-це-можливо-advisory">Rule 8.13 - вказівник на const, коли це можливо (Advisory)<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#rule-813---%D0%B2%D0%BA%D0%B0%D0%B7%D1%96%D0%B2%D0%BD%D0%B8%D0%BA-%D0%BD%D0%B0-const-%D0%BA%D0%BE%D0%BB%D0%B8-%D1%86%D0%B5-%D0%BC%D0%BE%D0%B6%D0%BB%D0%B8%D0%B2%D0%BE-advisory" class="hash-link" aria-label="Пряме посилання на Rule 8.13 - вказівник на const, коли це можливо (Advisory)" title="Пряме посилання на Rule 8.13 - вказівник на const, коли це можливо (Advisory)" translate="no">​</a></h4>
<p><strong>Правило:</strong> якщо функція не змінює дані за вказівником, вказівник оголошується як вказівник на <code>const</code>.</p>
<p><strong>Що ви отримуєте:</strong> сигнатура — це контракт. Викликач бачить із прототипу, що функція лише читає, а компілятор це контролює. <code>const</code> буфер із flash можна передати лише у функцію з <code>const</code>, інакше код не скомпілюється (або, гірше, запише у flash і спричинить fault).</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Checksum_Bad</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> sum </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">for</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> index </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> index </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> index</span><span class="token operator" style="color:rgb(137, 221, 255)">++</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> sum </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">sum </span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain"> data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">index</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> sum</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Checksum_Good</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> sum </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">for</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> index </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> index </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> index</span><span class="token operator" style="color:rgb(137, 221, 255)">++</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> sum </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">sum </span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain"> data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">index</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> sum</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="правила-про-код-який-ви-написали-і-код-якого-немає">Правила про код, який ви написали, і код, якого немає<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#%D0%BF%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%B0-%D0%BF%D1%80%D0%BE-%D0%BA%D0%BE%D0%B4-%D1%8F%D0%BA%D0%B8%D0%B9-%D0%B2%D0%B8-%D0%BD%D0%B0%D0%BF%D0%B8%D1%81%D0%B0%D0%BB%D0%B8-%D1%96-%D0%BA%D0%BE%D0%B4-%D1%8F%D0%BA%D0%BE%D0%B3%D0%BE-%D0%BD%D0%B5%D0%BC%D0%B0%D1%94" class="hash-link" aria-label="Пряме посилання на Правила про код, який ви написали, і код, якого немає" title="Пряме посилання на Правила про код, який ви написали, і код, якого немає" translate="no">​</a></h3>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="rule-177-і-directive-47---не-ігноруйте-результати-required">Rule 17.7 і Directive 4.7 - не ігноруйте результати (Required)<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#rule-177-%D1%96-directive-47---%D0%BD%D0%B5-%D1%96%D0%B3%D0%BD%D0%BE%D1%80%D1%83%D0%B9%D1%82%D0%B5-%D1%80%D0%B5%D0%B7%D1%83%D0%BB%D1%8C%D1%82%D0%B0%D1%82%D0%B8-required" class="hash-link" aria-label="Пряме посилання на Rule 17.7 і Directive 4.7 - не ігноруйте результати (Required)" title="Пряме посилання на Rule 17.7 і Directive 4.7 - не ігноруйте результати (Required)" translate="no">​</a></h4>
<p><strong>Правила:</strong> повернене значення має бути використане (17.7), а коли функція повертає код помилки, його треба перевірити (4.7).</p>
<p><strong>Що ви отримуєте:</strong> Проігноровані повернені значення — це проігноровані помилки. Надсилання кадру на зайнятий UART, відмова запису у flash, тайм-аут I2C: усе це проходить мовчки, і застосунок продовжує працювати з хибним припущенням. Якщо вам справді байдуже, скажіть це за допомогою <code>(void)</code>, щоб читач (і аналізатор) знав, що це було рішення, а не помилка:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">bool </span><span class="token function" style="color:rgb(130, 170, 255)">Send_Checked</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">UART_OK </span><span class="token operator" style="color:rgb(137, 221, 255)">!=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Uart_Send</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> false</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> true</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token function" style="color:rgb(130, 170, 255)">Uart_Send</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">message</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">sizeof</span><span class="token plain"> message</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* a log message, loss is acceptable */</span><br></div></code></pre></div></div>
<p><strong>Хто це знаходить:</strong> аналізатор, у gcc — з <code>__attribute__((warn_unused_result))</code> на вашому власному API.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="rules-21-і-22---жодного-недосяжного-і-мертвого-коду-required">Rules 2.1 і 2.2 - жодного недосяжного і мертвого коду (Required)<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#rules-21-%D1%96-22---%D0%B6%D0%BE%D0%B4%D0%BD%D0%BE%D0%B3%D0%BE-%D0%BD%D0%B5%D0%B4%D0%BE%D1%81%D1%8F%D0%B6%D0%BD%D0%BE%D0%B3%D0%BE-%D1%96-%D0%BC%D0%B5%D1%80%D1%82%D0%B2%D0%BE%D0%B3%D0%BE-%D0%BA%D0%BE%D0%B4%D1%83-required" class="hash-link" aria-label="Пряме посилання на Rules 2.1 і 2.2 - жодного недосяжного і мертвого коду (Required)" title="Пряме посилання на Rules 2.1 і 2.2 - жодного недосяжного і мертвого коду (Required)" translate="no">​</a></h4>
<p><strong>Правила:</strong> проєкт не повинен містити код, який ніколи не може виконатися (2.1), і код, який виконується, але не впливає на результат (2.2).</p>
<p><strong>Що ви отримуєте:</strong> обидва види — сигнал помилки: умова, що ніколи не настає, забутий <code>return</code> або помилка копіювання й вставлення. Мертвий код також спотворює покриття тестами (не можна досягти 100 % рядків, які не виконує жоден вхід) і змушує читача про нього думати. Обидва види є в цій функції:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Level_Get</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> raw</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> level </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">3u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">                </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* dead: overwritten before it is read */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    level </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">raw </span><span class="token operator" style="color:rgb(137, 221, 255)">/</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">2u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> level</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Level_Log</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">raw</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">                    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* unreachable */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Перше присвоєння <code>level</code> мертве, а останній рядок недосяжний.</p>
<p><strong>Хто це знаходить:</strong> аналізатор. gcc з <code>-Wall -Wextra</code> у моєму тесті промовчав.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="rule-207-і-directive-49---макроси-з-параметрами-required--advisory">Rule 20.7 і Directive 4.9 - макроси з параметрами (Required / Advisory)<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#rule-207-%D1%96-directive-49---%D0%BC%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D0%B8-%D0%B7-%D0%BF%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80%D0%B0%D0%BC%D0%B8-required--advisory" class="hash-link" aria-label="Пряме посилання на Rule 20.7 і Directive 4.9 - макроси з параметрами (Required / Advisory)" title="Пряме посилання на Rule 20.7 і Directive 4.9 - макроси з параметрами (Required / Advisory)" translate="no">​</a></h4>
<p><strong>Правило:</strong> розгорнутий параметр макросу має бути в дужках (20.7), а функції віддається перевага перед функціоподібним макросом (4.9).</p>
<p><strong>Що ви отримуєте:</strong> макрос — це текстова заміна. Якщо ви не захистите параметр, пріоритет визначає <em>викликач</em>:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name function" style="color:rgb(130, 170, 255)">SQUARE_BAD</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression">x</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token macro property expression">  x </span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">*</span><span class="token macro property expression"> x</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name function" style="color:rgb(130, 170, 255)">SQUARE_OK</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression">x</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token macro property expression">   </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression">x</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token macro property expression"> </span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">*</span><span class="token macro property expression"> </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression">x</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">SQUARE_BAD</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">2</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* 2 + 1 * 2 + 1 = 5, not 9 */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">SQUARE_OK</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">2</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ((2 + 1) * (2 + 1)) = 9 */</span><br></div></code></pre></div></div>
<p>Навіть захищений макрос має проблему: <code>SQUARE_OK(count++)</code> інкрементує двічі. Тому директива 4.9 рекомендує функцію. Inline-функція має тип, обчислює аргумент один раз і коштує стільки ж:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">inline</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Square</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> value </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="використання-misra-у-реальному-проєкті">Використання MISRA у реальному проєкті<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#%D0%B2%D0%B8%D0%BA%D0%BE%D1%80%D0%B8%D1%81%D1%82%D0%B0%D0%BD%D0%BD%D1%8F-misra-%D1%83-%D1%80%D0%B5%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83-%D0%BF%D1%80%D0%BE%D1%94%D0%BA%D1%82%D1%96" class="hash-link" aria-label="Пряме посилання на Використання MISRA у реальному проєкті" title="Пряме посилання на Використання MISRA у реальному проєкті" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="попередження-компілятора--перший-крок-а-не-останній">Попередження компілятора — перший крок, а не останній<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#%D0%BF%D0%BE%D0%BF%D0%B5%D1%80%D0%B5%D0%B4%D0%B6%D0%B5%D0%BD%D0%BD%D1%8F-%D0%BA%D0%BE%D0%BC%D0%BF%D1%96%D0%BB%D1%8F%D1%82%D0%BE%D1%80%D0%B0--%D0%BF%D0%B5%D1%80%D1%88%D0%B8%D0%B9-%D0%BA%D1%80%D0%BE%D0%BA-%D0%B0-%D0%BD%D0%B5-%D0%BE%D1%81%D1%82%D0%B0%D0%BD%D0%BD%D1%96%D0%B9" class="hash-link" aria-label="Пряме посилання на Попередження компілятора — перший крок, а не останній" title="Пряме посилання на Попередження компілятора — перший крок, а не останній" translate="no">​</a></h3>
<p>Я скомпілював усі наведені вище приклади з <code>gcc -Wall -Wextra -Wconversion -Wshadow -Wswitch-default -Wimplicit-fallthrough -O2</code>. gcc видав п'ять попереджень, і вони стосуються лише трьох «поганих» прикладів: порівняння з розширеним доповненням, <code>switch</code> (відсутній <code>default</code>, необроблене значення enum і fall-through) та відсутні дужки навколо <code>==</code> у перевірці маски. Він промовчав про звуження, неініціалізовану змінну, мертвий і недосяжний код, переповнення знакових чисел, рекурсію, приведення вказівника та макрос.</p>
<p>Моя рекомендація — компілятор з максимумом попереджень і <code>-Werror</code> у CI як базовий рівень. Це безкоштовно і усуває найочевидніші проблеми, але не замінює статичний аналіз.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="оберіть-інструмент-і-додайте-його-в-ci">Оберіть інструмент і додайте його в CI<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#%D0%BE%D0%B1%D0%B5%D1%80%D1%96%D1%82%D1%8C-%D1%96%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BC%D0%B5%D0%BD%D1%82-%D1%96-%D0%B4%D0%BE%D0%B4%D0%B0%D0%B9%D1%82%D0%B5-%D0%B9%D0%BE%D0%B3%D0%BE-%D0%B2-ci" class="hash-link" aria-label="Пряме посилання на Оберіть інструмент і додайте його в CI" title="Пряме посилання на Оберіть інструмент і додайте його в CI" translate="no">​</a></h3>
<p>Для перевірки правил потрібен статичний аналізатор. Є комерційні (Polyspace, Helix QAC, PC-lint Plus, Coverity, Parasoft C/C++test, LDRA, IAR C-STAT та інші) і безкоштовні, що покривають частину правил, наприклад <a href="https://cppcheck.sourceforge.io/" target="_blank" rel="noopener noreferrer" class="">cppcheck</a> з його addon для MISRA. Addon потребує на вході текст правил, і через авторське право його треба надати зі своєї копії стандарту. Інструмент ніколи не буває ідеальним: для невирішуваних правил будуть хибні спрацьовування, а іноді порушення буде пропущене.</p>
<p>Перевірка належить до CI кожного коміту, а не до ручного запуску перед релізом. Інакше ви робите ту саму роботу тричі: раз, коли пишете, раз, коли знаходять порушення, і раз, коли їх виправляєте.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="відхилення--частина-системи">Відхилення — частина системи<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#%D0%B2%D1%96%D0%B4%D1%85%D0%B8%D0%BB%D0%B5%D0%BD%D0%BD%D1%8F--%D1%87%D0%B0%D1%81%D1%82%D0%B8%D0%BD%D0%B0-%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B8" class="hash-link" aria-label="Пряме посилання на Відхилення — частина системи" title="Пряме посилання на Відхилення — частина системи" translate="no">​</a></h3>
<p>Іноді правило доводиться порушувати. Доступ до апаратного регістра потребує приведення цілого числа до вказівника, bootloader має записувати у flash через вказівник. MISRA це знає і визначає <strong>відхилення (deviation)</strong>: запис із правилом, місцем, причиною та оцінкою ризику. Запис має лежати поруч із кодом:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* MISRA deviation: Rule 11.4 (Advisory), see DEV-012</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * Reason: the address of the peripheral register is fixed by the hardware.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * Contained in this macro, the rest of the code uses the register type. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">UART_REGS</span><span class="token macro property"> </span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token macro property expression class-name" style="color:rgb(255, 203, 107)">uartRegisters_t</span><span class="token macro property expression"> </span><span class="token macro property expression operator" style="color:rgb(137, 221, 255)">*</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">0x40004400u</span><span class="token macro property expression punctuation" style="color:rgb(199, 146, 234)">)</span><br></div></code></pre></div></div>
<p>Відхилення — це не поразка. Перелік добре обґрунтованих відхилень — ознака проєкту, в якому люди думають. Проєкт без жодного відхилення і без доступу до апаратури зазвичай або бреше, або дуже малий.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="legacy-код-і-код-вендора">Legacy-код і код вендора<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#legacy-%D0%BA%D0%BE%D0%B4-%D1%96-%D0%BA%D0%BE%D0%B4-%D0%B2%D0%B5%D0%BD%D0%B4%D0%BE%D1%80%D0%B0" class="hash-link" aria-label="Пряме посилання на Legacy-код і код вендора" title="Пряме посилання на Legacy-код і код вендора" translate="no">​</a></h3>
<p>Ви не зробите 200 000 рядків старого проєкту сумісними за тиждень. Що працює:</p>
<ol>
<li class=""><strong>Не чіпайте старий код,</strong> увімкніть перевірку для <em>нових і змінених</em> файлів. Baseline аналізатора зберігає наявні порушення, і CI падає лише на нових.</li>
<li class=""><strong>Починайте з правил mandatory і required,</strong> advisory — пізніше.</li>
<li class=""><strong>Виключіть сторонній код</strong> і задокументуйте це. Згенерований код вендора (HAL, LL, CMSIS) виправляти не вам. Це ще одна причина для шарової архітектури зі статті про <a class="" href="https://embedbits.cz/uk/blog/design-architecture">дизайн та архітектуру</a>: коли код вендора лежить у RAL, а MCAL і все вище — ваш код, межа перевірки — це тека.</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="що-не-допомагає">Що не допомагає<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#%D1%89%D0%BE-%D0%BD%D0%B5-%D0%B4%D0%BE%D0%BF%D0%BE%D0%BC%D0%B0%D0%B3%D0%B0%D1%94" class="hash-link" aria-label="Пряме посилання на Що не допомагає" title="Пряме посилання на Що не допомагає" translate="no">​</a></h3>
<ul>
<li class=""><strong>Гонитва за кількістю порушень.</strong> Правило, виправлене так, що воно задовольняє інструмент, але ламає зміст (приведення типу, додане лише щоб заглушити попередження), гірше за оригінал.</li>
<li class=""><strong>MISRA як аргумент.</strong> «Так каже MISRA» — це не причина. Якщо ви не можете пояснити, від чого правило вас захищає, ви не зможете вирішити, коли настав час для відхилення.</li>
<li class=""><strong>Віра, що сумісність означає коректність.</strong> MISRA робить деякі помилки неможливими. Вимоги, архітектура, рев'ю та тести залишаються на вас.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="підсумок">Підсумок<a href="https://embedbits.cz/uk/blog/misra-c-rules-in-practice#%D0%BF%D1%96%D0%B4%D1%81%D1%83%D0%BC%D0%BE%D0%BA" class="hash-link" aria-label="Пряме посилання на Підсумок" title="Пряме посилання на Підсумок" translate="no">​</a></h2>
















































































<table><thead><tr><th>Правило</th><th>Коротко</th><th>Користь</th></tr></thead><tbody><tr><td>9.1</td><td>Ініціалізуйте перед читанням</td><td>Жодних випадкових значень, однакова поведінка в debug і release</td></tr><tr><td>10.x</td><td>Дотримуйтесь essential types</td><td>Жодних сюрпризів від integer promotion і звуження</td></tr><tr><td>Dir 4.6</td><td>Типи фіксованої ширини</td><td>Той самий код означає те саме на кожній цільовій платформі</td></tr><tr><td>1.3</td><td>Жодної undefined behavior</td><td>Оптимізатор не може прибрати ваші перевірки</td></tr><tr><td>14.4</td><td>Явні умови</td><td>Намір читається</td></tr><tr><td>12.1</td><td>Дужки</td><td>Жодних помилок із пріоритетом</td></tr><tr><td>15.6, 15.7, 16.3, 16.4</td><td>Фігурні дужки, <code>else</code>, <code>break</code>, <code>default</code></td><td>Обробляється кожен випадок</td></tr><tr><td>17.2</td><td>Жодної рекурсії</td><td>Використання стека можна обчислити</td></tr><tr><td>Dir 4.12, 21.3</td><td>Жодної динамічної пам'яті</td><td>Жодної фрагментації, витоків і сюрпризів у таймінгах</td></tr><tr><td>11.3, 11.5</td><td>Жодних трюків з типами вказівників</td><td>Жодних помилок вирівнювання й aliasing</td></tr><tr><td>8.13</td><td>Вказівники <code>const</code></td><td>Сигнатура — це контракт</td></tr><tr><td>17.7, Dir 4.7</td><td>Використовуйте повернені значення</td><td>Помилки не зникають</td></tr><tr><td>2.1, 2.2</td><td>Жодного недосяжного чи мертвого коду</td><td>Помилки видно, покриття має сенс</td></tr><tr><td>20.7, Dir 4.9</td><td>Безпечні макроси, надавайте перевагу функціям</td><td>Жодного прихованого подвійного обчислення чи помилок пріоритету</td></tr></tbody></table>
<p>Якщо ви запам'ятаєте лише одне: <strong>правило MISRA — це шрам від помилки, яку хтось уже мав.</strong> Вам не обов'язково любити правила, але добре знати, шрамом від чого є кожне з них. Тоді ви знатимете, коли його дотримуватися, а коли написати відхилення і взяти відповідальність на себе.</p>
<p>Описані тут правила — лише добірка. Повний набір містить понад 140 настанов, а решту (препроцесор, стандартну бібліотеку, структуру оголошень) варто один раз прочитати у вашій копії стандарту.</p>]]></content:encoded>
            <category>Вбудовані системи</category>
            <category>C</category>
            <category>MISRA</category>
        </item>
        <item>
            <title><![CDATA[Принципи SOLID: від класів C++ до звичайного C]]></title>
            <link>https://embedbits.cz/uk/blog/solid-principles-c-cpp</link>
            <guid>https://embedbits.cz/uk/blog/solid-principles-c-cpp</guid>
            <pubDate>Sat, 03 Oct 2026 21:00:00 GMT</pubDate>
            <description><![CDATA[П'ять літер, які, здається, є на кожній співбесіді з розробки програмного забезпечення. S, O, L, I та D. Принципи описано для об'єктно-орієнтованих мов, тож поширений висновок такий: «SOLID - для C++ та Java, ми пишемо на C, отже, нас це не стосується». Цей висновок хибний, і я спробую показати чому. Для кожного принципу ви знайдете оригінальну ідею з прикладом на C++, а потім ту саму ідею на C, без класів, без наслідування і без жодного virtual.]]></description>
            <content:encoded><![CDATA[<p>П'ять літер, які, здається, є на кожній співбесіді з розробки програмного забезпечення. S, O, L, I та D. Принципи описано для об'єктно-орієнтованих мов, тож поширений висновок такий: «SOLID - для C++ та Java, ми пишемо на C, отже, нас це не стосується». Цей висновок хибний, і я спробую показати чому. Для кожного принципу ви знайдете оригінальну ідею з прикладом на C++, а потім ту саму ідею на C, без класів, без наслідування і без жодного <code>virtual</code>.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="звідки-береться-solid">Звідки береться SOLID<a href="https://embedbits.cz/uk/blog/solid-principles-c-cpp#%D0%B7%D0%B2%D1%96%D0%B4%D0%BA%D0%B8-%D0%B1%D0%B5%D1%80%D0%B5%D1%82%D1%8C%D1%81%D1%8F-solid" class="hash-link" aria-label="Пряме посилання на Звідки береться SOLID" title="Пряме посилання на Звідки береться SOLID" translate="no">​</a></h2>
<p>Принципи зібрав Роберт К. Мартін («дядько Боб») на початку 2000-х, а саму абревіатуру вигадав Майкл Фезерс. Деякі ідеї значно старші: <em>принцип відкритості/закритості</em> походить від Бертрана Мейєра (1988), а <em>принцип підстановки Барбари Ліскоу</em> - від Барбари Ліскоу (1987).</p>
<p>Усі вони відповідають на одне запитання: <strong>як писати код, що переживає зміни?</strong> Не код, який працює сьогодні, а код, який можна розширювати, тестувати й підтримувати хлопцеві з цитати на першій сторінці, який знає, де ви живете.</p>
<p>Приклади на C++ використовують класи та віртуальні функції, бо саме так принципи пояснювали спочатку. Приклади на C дотримуються <a class="" href="https://embedbits.cz/uk/docs/platform/embi-platform/coding-style">мого стилю кодування</a>: імена <code>Module_Function</code>, типи <code>_t</code> і змінні зі змістовними іменами. Один виняток заради довжини: я використовую прості <code>uint8_t</code>, <code>uint32_t</code> і <code>float</code> там, де справжній проєкт мав би власні typedef (<code>temperature_t</code>, <code>voltage_t</code>), чого вимагає стиль кодування. Усі приклади в цій статті було скомпільовано (<code>gcc -std=c11 -Wall -Wextra -pedantic</code> та <code>g++ -std=c++17</code>) і запущено з невеликим модульним тестом, включно з «поганими».</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="чим-насправді-є-клас">Чим насправді є клас<a href="https://embedbits.cz/uk/blog/solid-principles-c-cpp#%D1%87%D0%B8%D0%BC-%D0%BD%D0%B0%D1%81%D0%BF%D1%80%D0%B0%D0%B2%D0%B4%D1%96-%D1%94-%D0%BA%D0%BB%D0%B0%D1%81" class="hash-link" aria-label="Пряме посилання на Чим насправді є клас" title="Пряме посилання на Чим насправді є клас" translate="no">​</a></h3>
<p>Перш ніж почати, коротке нагадування, що компілятор робить за лаштунками. Клас C++ з віртуальними функціями - це:</p>
<ul>
<li class=""><code>struct</code> з даними об'єкта,</li>
<li class="">прихований вказівник у цій структурі на таблицю вказівників на функції (<em>vtable</em>), по одній таблиці на клас,</li>
<li class="">домовленість, що кожен метод отримує вказівник на об'єкт першим параметром (<code>this</code>).</li>
</ul>
<p>У C ми можемо написати точнісінько те саме вручну: <code>struct</code> з даними, <code>const struct</code> з вказівниками на функції та <code>void *context</code> як заміну <code>this</code>. Пам'ятайте про це, уся стаття на цьому побудована. C++ не дає тут нової можливості, він лише пише за вас шаблонний код і перевіряє його під час компіляції.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="s---single-responsibility-principle-принцип-єдиної-відповідальності">S - Single Responsibility Principle (принцип єдиної відповідальності)<a href="https://embedbits.cz/uk/blog/solid-principles-c-cpp#s---single-responsibility-principle-%D0%BF%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF-%D1%94%D0%B4%D0%B8%D0%BD%D0%BE%D1%97-%D0%B2%D1%96%D0%B4%D0%BF%D0%BE%D0%B2%D1%96%D0%B4%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%96" class="hash-link" aria-label="Пряме посилання на S - Single Responsibility Principle (принцип єдиної відповідальності)" title="Пряме посилання на S - Single Responsibility Principle (принцип єдиної відповідальності)" translate="no">​</a></h2>
<blockquote>
<p>Модуль повинен мати одну, і лише одну, причину для змін.</p>
</blockquote>
<p>Важлива тут «причина для змін». Це не означає «клас має один метод». Це означає, що кожна частина коду відповідає перед одним джерелом змін: коли замінюють датчик, змінюється лише перетворення, коли замовник хоче інший формат журналу, змінюється лише форматування.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="на-c">На C++<a href="https://embedbits.cz/uk/blog/solid-principles-c-cpp#%D0%BD%D0%B0-c" class="hash-link" aria-label="Пряме посилання на На C++" title="Пряме посилання на На C++" translate="no">​</a></h3>
<p>Наступний клас робить усе: читає АЦП, перетворює значення, форматує текст і надсилає його. Чотири причини для змін в одному місці:</p>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">TemperatureMonitor</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Run</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">uint16_t</span><span class="token plain"> rawValue </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">AdcRead</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">                                </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// 1. hardware access</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> celsius </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">rawValue </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">3.3f</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">/</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">4095.0f</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">-</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0.5f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">100.0f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">  </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// 2. conversion</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token number" style="color:rgb(247, 140, 108)">24</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token function" style="color:rgb(130, 170, 255)">snprintf</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">sizeof</span><span class="token plain"> text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"T=%.1f C\r\n"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> celsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">          </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// 3. formatting</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token function" style="color:rgb(130, 170, 255)">UartSend</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">                                                     </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// 4. communication</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>Кожна зміна (новий датчик, журнал у CAN замість UART, інший формат тексту) править той самий клас, і нічого з цього не можна протестувати без hardware. Розв'язок - розділити відповідальності й дозволити монітору лише з'єднувати їх:</p>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">AdcChannel</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">uint16_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Read</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">AdcRead</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">Tmp36Converter</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">ToCelsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">uint16_t</span><span class="token plain"> rawValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">rawValue </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">3.3f</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">/</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">4095.0f</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">-</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0.5f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">100.0f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">UartLogger</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Print</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> celsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token number" style="color:rgb(247, 140, 108)">24</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token function" style="color:rgb(130, 170, 255)">snprintf</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">sizeof</span><span class="token plain"> text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"T=%.1f C\r\n"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> celsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token function" style="color:rgb(130, 170, 255)">UartSend</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">TemperatureMonitor</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Run</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> logger</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">Print</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">converter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">ToCelsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">adc</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">Read</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">private</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    AdcChannel   adc</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    Tmp36Converter converter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    UartLogger   logger</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="на-c-1">На C<a href="https://embedbits.cz/uk/blog/solid-principles-c-cpp#%D0%BD%D0%B0-c-1" class="hash-link" aria-label="Пряме посилання на На C" title="Пряме посилання на На C" translate="no">​</a></h3>
<p>Точно така сама помилка можлива, і поширена, у звичайному C, де зазвичай виглядає як одна довга функція в <code>main.c</code>:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Monitor_Run</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint16_t</span><span class="token plain"> rawValue </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Adc_Read</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">                                </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// 1. hardware access</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> celsius </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">rawValue </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">3.3f</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">/</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">4095.0f</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">-</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0.5f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">100.0f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">  </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// 2. conversion</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token number" style="color:rgb(247, 140, 108)">24</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">snprintf</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">sizeof</span><span class="token plain"> text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"T=%.1f C\r\n"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> celsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">                </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// 3. formatting</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Uart_Send</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">                                                     </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// 4. communication</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Інструмент розділення в C - це <strong>модуль</strong>: пара файлів <code>.h</code>/<code>.c</code> зі спільним префіксом, де заголовок - публічний інтерфейс, а все інше - <code>static</code>. Один модуль, одна відповідальність:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Tmp36.h</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifndef</span><span class="token macro property"> </span><span class="token macro property expression">TMP36_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">TMP36_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* Converts a raw ADC value of the TMP36 sensor into degrees Celsius. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Tmp36_Get_Celsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint16_t</span><span class="token plain"> rawValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><br></div></code></pre></div></div>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Tmp36.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Tmp36.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Tmp36_Get_Celsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint16_t</span><span class="token plain"> rawValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">rawValue </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">3.3f</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">/</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">4095.0f</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">-</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0.5f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">100.0f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Logger.h</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifndef</span><span class="token macro property"> </span><span class="token macro property expression">LOGGER_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">LOGGER_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Logger_Print_Temperature</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> celsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><br></div></code></pre></div></div>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Logger.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Logger.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Logger_Print_Temperature</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> celsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token number" style="color:rgb(247, 140, 108)">24</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">snprintf</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">sizeof</span><span class="token plain"> text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"T=%.1f C\r\n"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> celsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Uart_Send</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Модуль АЦП (<code>Adc.h</code>) - та сама ідея, а монітор лише з'єднує модулі між собою:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Monitor.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Adc.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Logger.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Tmp36.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Monitor_Run</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Logger_Print_Temperature</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token function" style="color:rgb(130, 170, 255)">Tmp36_Get_Celsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token function" style="color:rgb(130, 170, 255)">Adc_Read</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Якщо TMP36 замінюють на NTC, міняється лише <code>Tmp36.c</code>. Якщо журнал іде в CAN замість UART, змінюється лише <code>Logger.c</code>. А <code>Tmp36_Get_Celsius()</code> можна протестувати на вашому PC звичайним <code>assert</code>, бо вона не торкається жодного регістра.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="o---openclosed-principle-принцип-відкритостізакритості">O - Open/Closed Principle (принцип відкритості/закритості)<a href="https://embedbits.cz/uk/blog/solid-principles-c-cpp#o---openclosed-principle-%D0%BF%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF-%D0%B2%D1%96%D0%B4%D0%BA%D1%80%D0%B8%D1%82%D0%BE%D1%81%D1%82%D1%96%D0%B7%D0%B0%D0%BA%D1%80%D0%B8%D1%82%D0%BE%D1%81%D1%82%D1%96" class="hash-link" aria-label="Пряме посилання на O - Open/Closed Principle (принцип відкритості/закритості)" title="Пряме посилання на O - Open/Closed Principle (принцип відкритості/закритості)" translate="no">​</a></h2>
<blockquote>
<p>Програмні сутності повинні бути відкритими для розширення, але закритими для модифікації.</p>
</blockquote>
<p>Ви повинні мати можливість додавати нову поведінку, не редагуючи код, який уже працює й протестований. Типовий запах - <code>switch</code> за типом, який росте з кожною новою функцією.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="на-c-2">На C++<a href="https://embedbits.cz/uk/blog/solid-principles-c-cpp#%D0%BD%D0%B0-c-2" class="hash-link" aria-label="Пряме посилання на На C++" title="Пряме посилання на На C++" translate="no">​</a></h3>
<p>Конвеєр нижче доводиться змінювати для кожного нового фільтра, включно з <code>enum</code> і <code>switch</code>:</p>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">enum</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">FilterType</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> MovingAverage</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> Median </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">SamplePipeline</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">explicit</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">SamplePipeline</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">FilterType type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">filterType</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Feed</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">switch</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">filterType</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">            </span><span class="token keyword" style="font-style:italic">case</span><span class="token plain"> FilterType</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">MovingAverage</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">MovingAverageStep</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">            </span><span class="token keyword" style="font-style:italic">case</span><span class="token plain"> FilterType</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">Median</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain">        </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">MedianStep</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// every new filter means editing this class again</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">private</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    FilterType filterType</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">MovingAverageStep</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">MedianStep</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>Рішення - абстракція (інтерфейс), яку знає конвеєр і яку реалізують нові фільтри:</p>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">IFilter</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">virtual</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">~</span><span class="token function" style="color:rgb(130, 170, 255)">IFilter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">default</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">virtual</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Process</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">MovingAverage</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token base-clause keyword" style="font-style:italic">public</span><span class="token base-clause"> </span><span class="token base-clause class-name" style="color:rgb(255, 203, 107)">IFilter</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Process</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">override</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        sum </span><span class="token operator" style="color:rgb(137, 221, 255)">+=</span><span class="token plain"> sample </span><span class="token operator" style="color:rgb(137, 221, 255)">-</span><span class="token plain"> window</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">index</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        window</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">index</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        index </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">index </span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">%</span><span class="token plain"> window</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">size</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> sum </span><span class="token operator" style="color:rgb(137, 221, 255)">/</span><span class="token plain"> window</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">size</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">private</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">array</span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token keyword" style="font-style:italic">float</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">4</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain"> window</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">size_t index </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> sum </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0.0f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">Median3</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token base-clause keyword" style="font-style:italic">public</span><span class="token base-clause"> </span><span class="token base-clause class-name" style="color:rgb(255, 203, 107)">IFilter</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Process</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">override</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        last</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">index</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        index </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">index </span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">%</span><span class="token plain"> last</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">size</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">auto</span><span class="token plain"> sorted </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> last</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token function" style="color:rgb(130, 170, 255)">sort</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">sorted</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">begin</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> sorted</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">end</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> sorted</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">private</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">array</span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token keyword" style="font-style:italic">float</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">3</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain"> last</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">size_t index </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">SamplePipeline</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">explicit</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">SamplePipeline</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">IFilter</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain"> filterToUse</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">filter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">filterToUse</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Feed</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> filter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">Process</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// never changes again</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">private</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    IFilter</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain"> filter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>Новий фільтр - це новий клас. <code>SamplePipeline</code> закритий для модифікації, а система відкрита для розширення.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="на-c-3">На C<a href="https://embedbits.cz/uk/blog/solid-principles-c-cpp#%D0%BD%D0%B0-c-3" class="hash-link" aria-label="Пряме посилання на На C" title="Пряме посилання на На C" translate="no">​</a></h3>
<p>Та сама проблема з <code>switch</code> у C:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">enum</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> FILTER_MOVING_AVERAGE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> FILTER_MEDIAN </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">filterType_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Pipeline_Feed</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">filterType_t</span><span class="token plain"> type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">switch</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">case</span><span class="token plain"> FILTER_MOVING_AVERAGE</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">MovingAverage_Step</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">case</span><span class="token plain"> FILTER_MEDIAN</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain">         </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Median_Step</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* every new filter means editing this function again */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Замінник віртуальної таблиці в C - це <strong>таблиця вказівників на функції</strong> та <code>void *context</code> для даних екземпляра. Абстракція визначається один раз:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Filter.h</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifndef</span><span class="token macro property"> </span><span class="token macro property expression">FILTER_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">FILTER_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* The "interface": a table of functions and the data they work on. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">struct</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">Process</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">context</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token class-name" style="color:rgb(255, 203, 107)">filterOps_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">struct</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">filterOps_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">ops</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain">              </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">context</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token class-name" style="color:rgb(255, 203, 107)">filter_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">inline</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Filter_Process</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">filter_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">filter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> filter</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">ops</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token function" style="color:rgb(130, 170, 255)">Process</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">filter</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">context</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><br></div></code></pre></div></div>
<p>Кожен фільтр - це пара з даних (<code>context</code>) і константної таблиці з поведінкою:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">MovingAverage.h</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifndef</span><span class="token macro property"> </span><span class="token macro property expression">MOVING_AVERAGE_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">MOVING_AVERAGE_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Filter.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">MOVING_AVERAGE_SIZE</span><span class="token macro property"> </span><span class="token macro property expression number" style="color:rgb(247, 140, 108)">4u</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">struct</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain">    window</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">MOVING_AVERAGE_SIZE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain">    sum</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">unsigned</span><span class="token plain"> index</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token class-name" style="color:rgb(255, 203, 107)">movingAverage_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">extern</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">filterOps_t</span><span class="token plain"> movingAverageOps</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><br></div></code></pre></div></div>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">MovingAverage.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"MovingAverage.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">MovingAverage_Process</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">context</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">movingAverage_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">self </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> context</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    self</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">sum </span><span class="token operator" style="color:rgb(137, 221, 255)">+=</span><span class="token plain"> sample </span><span class="token operator" style="color:rgb(137, 221, 255)">-</span><span class="token plain"> self</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">window</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">self</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">index</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    self</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">window</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">self</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">index</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    self</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">index </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">self</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">index </span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">%</span><span class="token plain"> MOVING_AVERAGE_SIZE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> self</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">sum </span><span class="token operator" style="color:rgb(137, 221, 255)">/</span><span class="token plain"> MOVING_AVERAGE_SIZE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">filterOps_t</span><span class="token plain"> movingAverageOps </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">Process </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> MovingAverage_Process </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>Другий фільтр - це цілком окремий модуль, перший не чіпали:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Median3.h</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifndef</span><span class="token macro property"> </span><span class="token macro property expression">MEDIAN3_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">MEDIAN3_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Filter.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">struct</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain">    last</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token number" style="color:rgb(247, 140, 108)">3</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">unsigned</span><span class="token plain"> index</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token class-name" style="color:rgb(255, 203, 107)">median3_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">extern</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">filterOps_t</span><span class="token plain"> median3Ops</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><br></div></code></pre></div></div>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Median3.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Median3.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Median3_Process</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">context</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">median3_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">self </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> context</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    self</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">last</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">self</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">index</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    self</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">index </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">self</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">index </span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">%</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">3u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> first </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> self</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">last</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> second </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> self</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">last</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> third </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> self</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">last</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token number" style="color:rgb(247, 140, 108)">2</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">first </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;=</span><span class="token plain"> second </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;&amp;</span><span class="token plain"> second </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;=</span><span class="token plain"> third</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">||</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">third </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;=</span><span class="token plain"> second </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;&amp;</span><span class="token plain"> second </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;=</span><span class="token plain"> first</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> second</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">second </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;=</span><span class="token plain"> first </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;&amp;</span><span class="token plain"> first </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;=</span><span class="token plain"> third</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">||</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">third </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;=</span><span class="token plain"> first </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;&amp;</span><span class="token plain"> first </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;=</span><span class="token plain"> second</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> first</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> third</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">filterOps_t</span><span class="token plain"> median3Ops </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">Process </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> Median3_Process </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>Конвеєр знає лише <code>filter_t</code>, тож його більше ніколи не потрібно змінювати:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Pipeline.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Filter.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Pipeline_Feed</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">filter_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">filter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Filter_Process</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">filter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* never changes again */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>А ось як це використовується. Обидва фільтри працюють з тим самим конвеєром:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token class-name" style="color:rgb(255, 203, 107)">movingAverage_t</span><span class="token plain"> average </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">filter_t</span><span class="token plain"> averageFilter </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">movingAverageOps</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">average </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token class-name" style="color:rgb(255, 203, 107)">median3_t</span><span class="token plain"> median </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">filter_t</span><span class="token plain"> medianFilter </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">median3Ops</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">median </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> output </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Pipeline_Feed</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">medianFilter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> sample</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>Таблиці <code>const</code>, тому живуть у flash і не коштують RAM. Те, що компілятор C++ генерує для <code>virtual</code>, - це саме воно.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="l---liskov-substitution-principle-принцип-підстановки-ліскоу">L - Liskov Substitution Principle (принцип підстановки Ліскоу)<a href="https://embedbits.cz/uk/blog/solid-principles-c-cpp#l---liskov-substitution-principle-%D0%BF%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF-%D0%BF%D1%96%D0%B4%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B8-%D0%BB%D1%96%D1%81%D0%BA%D0%BE%D1%83" class="hash-link" aria-label="Пряме посилання на L - Liskov Substitution Principle (принцип підстановки Ліскоу)" title="Пряме посилання на L - Liskov Substitution Principle (принцип підстановки Ліскоу)" translate="no">​</a></h2>
<blockquote>
<p>Об'єкти підтипу повинні бути придатними всюди, де очікується базовий тип, так, щоб викликач не помічав різниці.</p>
</blockquote>
<p>Це найменш зрозумілий принцип. Він не про синтаксис (його перевіряє компілятор), а про <strong>контракт</strong>. Похідний клас не повинен вимагати більше, ніж обіцяє базовий, і не повинен постачати менше. Якщо викликачу потрібен <code>if (typeid(...))</code>, принцип порушено.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="на-c-4">На C++<a href="https://embedbits.cz/uk/blog/solid-principles-c-cpp#%D0%BD%D0%B0-c-4" class="hash-link" aria-label="Пряме посилання на На C++" title="Пряме посилання на На C++" translate="no">​</a></h3>
<p>Три пристрої зберігання за одним інтерфейсом. Який із них порушує контракт?</p>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">IStorage</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">virtual</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">~</span><span class="token function" style="color:rgb(130, 170, 255)">IStorage</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">default</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">virtual</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">bool</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Write</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">uint32_t</span><span class="token plain"> address</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">size_t length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">Eeprom</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token base-clause keyword" style="font-style:italic">public</span><span class="token base-clause"> </span><span class="token base-clause class-name" style="color:rgb(255, 203, 107)">IStorage</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">bool</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Write</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">uint32_t</span><span class="token plain"> address</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">size_t length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">override</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// any address, any length</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">Flash</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token base-clause keyword" style="font-style:italic">public</span><span class="token base-clause"> </span><span class="token base-clause class-name" style="color:rgb(255, 203, 107)">IStorage</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">bool</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Write</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">uint32_t</span><span class="token plain"> address</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">size_t length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">override</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">address </span><span class="token operator" style="color:rgb(137, 221, 255)">%</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">8</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">!=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">||</span><span class="token plain"> length </span><span class="token operator" style="color:rgb(137, 221, 255)">%</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">8</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">!=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">            </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 88, 116)">false</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// stronger precondition than the base class promised</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">ProgramDoubleWords</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">address</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">RomImage</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token base-clause keyword" style="font-style:italic">public</span><span class="token base-clause"> </span><span class="token base-clause class-name" style="color:rgb(255, 203, 107)">IStorage</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">bool</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Write</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">uint32_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">size_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">override</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">throw</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token function" style="color:rgb(130, 170, 255)">logic_error</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"ROM is read-only"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// the caller never expected an exception</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p><code>Flash</code> вимагає вирівняних адрес і довжин, чого базовий клас ніколи не згадував, тож код, який зберігає п'ять байтів, працює з <code>Eeprom</code> і мовчки провалюється з <code>Flash</code>. <code>RomImage</code> кидає виняток у системі, яка, імовірно, збирається з <code>-fno-exceptions</code>. Усі три компілюються, і лише один поводиться як слід.</p>
<p>Виправлення - дотримуватися контракту в реалізації та не наслідувати, коли можливості немає:</p>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">IReadable</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">virtual</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">~</span><span class="token function" style="color:rgb(130, 170, 255)">IReadable</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">default</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">virtual</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">bool</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Read</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">uint32_t</span><span class="token plain"> address</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">size_t length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">IWritable</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token base-clause keyword" style="font-style:italic">public</span><span class="token base-clause"> </span><span class="token base-clause class-name" style="color:rgb(255, 203, 107)">IReadable</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// Contract: any address, any length inside the device. Returns false only on a real failure.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">virtual</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">bool</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Write</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">uint32_t</span><span class="token plain"> address</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">size_t length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">Flash</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token base-clause keyword" style="font-style:italic">public</span><span class="token base-clause"> </span><span class="token base-clause class-name" style="color:rgb(255, 203, 107)">IWritable</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">bool</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Read</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">uint32_t</span><span class="token plain"> address</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">size_t length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">override</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">bool</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Write</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">uint32_t</span><span class="token plain"> address</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">size_t length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">override</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// Honours the contract: unaligned parts are merged with the current content (read-modify-write).</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">ProgramWithPadding</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">address</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">RomImage</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token base-clause keyword" style="font-style:italic">public</span><span class="token base-clause"> </span><span class="token base-clause class-name" style="color:rgb(255, 203, 107)">IReadable</span><span class="token base-clause">   </span><span class="token base-clause comment" style="color:rgb(105, 112, 152);font-style:italic">// cannot write, so it is not an IWritable</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">bool</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Read</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">uint32_t</span><span class="token plain"> address</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">size_t length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">override</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="на-c-5">На C<a href="https://embedbits.cz/uk/blog/solid-principles-c-cpp#%D0%BD%D0%B0-c-5" class="hash-link" aria-label="Пряме посилання на На C" title="Пряме посилання на На C" translate="no">​</a></h3>
<p>У C немає наслідування, тож принцип нерелевантний? Навпаки, таблиця вказівників на функції - це інтерфейс так само, як у C++, і проблема підстановки та сама. У C вона навіть небезпечніша, бо типові порушення - це <code>NULL</code> у таблиці або <code>assert</code> у реалізації:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">struct</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">bool</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">Read</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> address</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">bool</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">Write</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> address</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token class-name" style="color:rgb(255, 203, 107)">storageOps_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> bool </span><span class="token function" style="color:rgb(130, 170, 255)">Flash_Write</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> address</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">assert</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">address </span><span class="token operator" style="color:rgb(137, 221, 255)">%</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">8u</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;&amp;</span><span class="token plain"> length </span><span class="token operator" style="color:rgb(137, 221, 255)">%</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">8u</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* the caller was never told */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Flash_ProgramDoubleWords</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">address</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">storageOps_t</span><span class="token plain"> flashStorage </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">Read </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> Flash_Read</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">Write </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> Flash_Write </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">storageOps_t</span><span class="token plain"> romStorage   </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">Read </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> Rom_Read</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">   </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">Write </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token constant" style="color:rgb(130, 170, 255)">NULL</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* callers crash */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">bool </span><span class="token function" style="color:rgb(130, 170, 255)">Settings_Save</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">storageOps_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">storage</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> storage</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token function" style="color:rgb(130, 170, 255)">Write</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">SETTINGS_ADDRESS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* NULL call or failed assert */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Ліки ті самі, що й у C++, плюс одна звичка: <strong>запишіть контракт у заголовку</strong> і змусьте всі реалізації його виконувати. Жодного <code>NULL</code> у таблиці, жодного assert на помилку викликача, жодної прихованої передумови:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Storage.h</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifndef</span><span class="token macro property"> </span><span class="token macro property expression">STORAGE_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">STORAGE_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdint.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stddef.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">enum</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    STORAGE_OK</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    STORAGE_ERROR_READ_ONLY</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    STORAGE_ERROR_RANGE</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    STORAGE_ERROR_DEVICE</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token class-name" style="color:rgb(255, 203, 107)">storageStatus_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/*</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> * Contract of every storage implementation:</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> *  - Read and Write are never NULL.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> *  - Write accepts any address and any length inside the device. Alignment is the problem</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> *    of the implementation, not of the caller.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> *  - Writing to a read-only device does not crash, it returns STORAGE_ERROR_READ_ONLY.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic"> */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">struct</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">storageStatus_t</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">Read</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> address</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">storageStatus_t</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">Write</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> address</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token class-name" style="color:rgb(255, 203, 107)">storageOps_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><br></div></code></pre></div></div>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Storage.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">storageStatus_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Flash_Write</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> address</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* Unaligned head and tail are merged with the current content (read-modify-write). */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Flash_ProgramWithPadding</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">address</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">storageStatus_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Rom_Write</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> address</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">address</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> STORAGE_ERROR_READ_ONLY</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">storageOps_t</span><span class="token plain"> flashStorage </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">Read </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> Flash_Read</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">Write </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> Flash_Write </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">storageOps_t</span><span class="token plain"> romStorage   </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">Read </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> Rom_Read</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain">   </span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">Write </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> Rom_Write   </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token class-name" style="color:rgb(255, 203, 107)">storageStatus_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Settings_Save</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">storageOps_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">storage</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> storage</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token function" style="color:rgb(130, 170, 255)">Write</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">SETTINGS_ADDRESS</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* works with every implementation */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p><code>Settings_Save()</code> працює з flash, з образом ROM (він отримує нормальний код помилки) і з будь-якою реалізацією, що з'явиться пізніше.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="i---interface-segregation-principle-принцип-розділення-інтерфейсів">I - Interface Segregation Principle (принцип розділення інтерфейсів)<a href="https://embedbits.cz/uk/blog/solid-principles-c-cpp#i---interface-segregation-principle-%D0%BF%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF-%D1%80%D0%BE%D0%B7%D0%B4%D1%96%D0%BB%D0%B5%D0%BD%D0%BD%D1%8F-%D1%96%D0%BD%D1%82%D0%B5%D1%80%D1%84%D0%B5%D0%B9%D1%81%D1%96%D0%B2" class="hash-link" aria-label="Пряме посилання на I - Interface Segregation Principle (принцип розділення інтерфейсів)" title="Пряме посилання на I - Interface Segregation Principle (принцип розділення інтерфейсів)" translate="no">​</a></h2>
<blockquote>
<p>Клієнтів не слід змушувати залежати від методів, якими вони не користуються.</p>
</blockquote>
<p>«Товстий» інтерфейс зв'язує всіх з усім. Коли UART отримує новий метод, кожен клієнт і кожен mock треба перекомпілювати й переглянути, хоча вони хотіли лише надсилати байти.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="на-c-6">На C++<a href="https://embedbits.cz/uk/blog/solid-principles-c-cpp#%D0%BD%D0%B0-c-6" class="hash-link" aria-label="Пряме посилання на На C++" title="Пряме посилання на На C++" translate="no">​</a></h3>
<p>Логеру потрібен один метод, але він залежить від п'яти:</p>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">IUart</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">virtual</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">~</span><span class="token function" style="color:rgb(130, 170, 255)">IUart</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">default</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">virtual</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain">        </span><span class="token function" style="color:rgb(130, 170, 255)">Init</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">uint32_t</span><span class="token plain"> baudrate</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">virtual</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain">        </span><span class="token function" style="color:rgb(130, 170, 255)">SetParity</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">Parity parity</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">virtual</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain">        </span><span class="token function" style="color:rgb(130, 170, 255)">EnableDma</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">bool</span><span class="token plain"> enable</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">virtual</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain">        </span><span class="token function" style="color:rgb(130, 170, 255)">Send</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">size_t length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">virtual</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">size_t </span><span class="token function" style="color:rgb(130, 170, 255)">Receive</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">size_t maxLength</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">Logger</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">explicit</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Logger</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">IUart</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain"> uartToUse</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">uart</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">uartToUse</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// needs one method, depends on five</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Print</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> uart</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">Send</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token generic-function function" style="color:rgb(130, 170, 255)">reinterpret_cast</span><span class="token generic-function generic class-name operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token generic-function generic class-name keyword" style="color:rgb(255, 203, 107);font-style:italic">const</span><span class="token generic-function generic class-name" style="color:rgb(255, 203, 107)"> </span><span class="token generic-function generic class-name keyword" style="color:rgb(255, 203, 107);font-style:italic">uint8_t</span><span class="token generic-function generic class-name" style="color:rgb(255, 203, 107)"> </span><span class="token generic-function generic class-name operator" style="color:rgb(137, 221, 255)">*</span><span class="token generic-function generic class-name operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token function" style="color:rgb(130, 170, 255)">strlen</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">private</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    IUart</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain"> uart</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>Інтерфейс розділено за роллю клієнта. Конфігурація лишається на конкретному класі, а логер просить лише можливість записувати байти:</p>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">IByteSink</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">virtual</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">~</span><span class="token function" style="color:rgb(130, 170, 255)">IByteSink</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">default</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">virtual</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Write</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">size_t length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">IByteSource</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">virtual</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">~</span><span class="token function" style="color:rgb(130, 170, 255)">IByteSource</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">default</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">virtual</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">size_t </span><span class="token function" style="color:rgb(130, 170, 255)">Read</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">size_t maxLength</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">Uart</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token base-clause keyword" style="font-style:italic">public</span><span class="token base-clause"> </span><span class="token base-clause class-name" style="color:rgb(255, 203, 107)">IByteSink</span><span class="token base-clause punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token base-clause"> </span><span class="token base-clause keyword" style="font-style:italic">public</span><span class="token base-clause"> </span><span class="token base-clause class-name" style="color:rgb(255, 203, 107)">IByteSource</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Init</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">uint32_t</span><span class="token plain"> baudrate</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">          </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// configuration stays on the concrete class</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">SetParity</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">Parity parity</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">EnableDma</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">bool</span><span class="token plain"> enable</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Write</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">size_t length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">override</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">size_t </span><span class="token function" style="color:rgb(130, 170, 255)">Read</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">size_t maxLength</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">override</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">Logger</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">explicit</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Logger</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">IByteSink</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain"> sinkToUse</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">sink</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">sinkToUse</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// exactly what it needs</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Print</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> sink</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">Write</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token generic-function function" style="color:rgb(130, 170, 255)">reinterpret_cast</span><span class="token generic-function generic class-name operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token generic-function generic class-name keyword" style="color:rgb(255, 203, 107);font-style:italic">const</span><span class="token generic-function generic class-name" style="color:rgb(255, 203, 107)"> </span><span class="token generic-function generic class-name keyword" style="color:rgb(255, 203, 107);font-style:italic">uint8_t</span><span class="token generic-function generic class-name" style="color:rgb(255, 203, 107)"> </span><span class="token generic-function generic class-name operator" style="color:rgb(137, 221, 255)">*</span><span class="token generic-function generic class-name operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token function" style="color:rgb(130, 170, 255)">strlen</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">private</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    IByteSink</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain"> sink</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>Mock для тесту логера тепер має один метод замість п'яти.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="на-c-7">На C<a href="https://embedbits.cz/uk/blog/solid-principles-c-cpp#%D0%BD%D0%B0-c-7" class="hash-link" aria-label="Пряме посилання на На C" title="Пряме посилання на На C" translate="no">​</a></h3>
<p>Товстий інтерфейс існує і в C: як величезна структура вказівників на функції або, частіше, як один гігантський заголовний файл <code>Uart.h</code>, який включають усі:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">struct</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain">   </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">Init</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> baudrate</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain">   </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">SetParity</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uartParity_t</span><span class="token plain"> parity</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain">   </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">EnableDma</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">bool enable</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain">   </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">Send</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">Receive</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> maxLength</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token class-name" style="color:rgb(255, 203, 107)">uartDriver_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Log_Init</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uartDriver_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">uart</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* needs Send, receives five functions */</span><br></div></code></pre></div></div>
<p>Є два інструменти. Перший такий самий, як у C++: малий інтерфейс (<code>byteSink_t</code>), що несе лише те, що потрібно клієнтові:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">ByteSink.h</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifndef</span><span class="token macro property"> </span><span class="token macro property expression">BYTE_SINK_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">BYTE_SINK_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stddef.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdint.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">struct</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">Write</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">context</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain">  </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">context</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token class-name" style="color:rgb(255, 203, 107)">byteSink_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><br></div></code></pre></div></div>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Log.h</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifndef</span><span class="token macro property"> </span><span class="token macro property expression">LOG_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">LOG_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"ByteSink.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Log_Init</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">byteSink_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">sink</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Log_Print</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><br></div></code></pre></div></div>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Log.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Log.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;string.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">byteSink_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">logSink</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Log_Init</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">byteSink_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">sink</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    logSink </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> sink</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Log_Print</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">char</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    logSink</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token function" style="color:rgb(130, 170, 255)">Write</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">logSink</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">context</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">strlen</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Другий специфічний для C: <strong>розділіть заголовок</strong>. Усі, хто лише надсилає байти, включають <code>Uart_Tx.h</code>, і ніхто інший не бачить конфігурації:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* Uart_Config.h  - used by the code that sets the peripheral up */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Uart_Init</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uart_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">uart</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> baudrate</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Uart_Set_Parity</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uart_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">uart</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uartParity_t</span><span class="token plain"> parity</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* Uart_Tx.h      - used by everybody who only sends */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Uart_Send</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uart_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">uart</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* Uart_Rx.h      - used by everybody who only receives */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Uart_Receive</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">uart_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">uart</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token plain"> maxLength</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>Логер можна протестувати трирядковою функцією, що перехоплює байти. Він не знає, що UART існує.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="d---dependency-inversion-principle-принцип-інверсії-залежностей">D - Dependency Inversion Principle (принцип інверсії залежностей)<a href="https://embedbits.cz/uk/blog/solid-principles-c-cpp#d---dependency-inversion-principle-%D0%BF%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF-%D1%96%D0%BD%D0%B2%D0%B5%D1%80%D1%81%D1%96%D1%97-%D0%B7%D0%B0%D0%BB%D0%B5%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D0%B5%D0%B9" class="hash-link" aria-label="Пряме посилання на D - Dependency Inversion Principle (принцип інверсії залежностей)" title="Пряме посилання на D - Dependency Inversion Principle (принцип інверсії залежностей)" translate="no">​</a></h2>
<blockquote>
<p>Модулі високого рівня не повинні залежати від модулів низького рівня. Обидва повинні залежати від абстракцій. Абстракції не повинні залежати від деталей.</p>
</blockquote>
<p>Це принцип, що пов'язує решту чотирьох. Саме він стоїть за шарами зі статті про <a class="" href="https://embedbits.cz/uk/blog/design-architecture">проєктування та архітектуру</a>: застосунок не повинен знати, який регістр перемикає світлодіод.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="на-c-8">На C++<a href="https://embedbits.cz/uk/blog/solid-principles-c-cpp#%D0%BD%D0%B0-c-8" class="hash-link" aria-label="Пряме посилання на На C++" title="Пряме посилання на На C++" translate="no">​</a></h3>
<p>Термостат (політика високого рівня) містить класи АЦП і GPIO (деталі низького рівня). Його не можна скомпілювати без hardware, не можна протестувати на PC і не можна перенести на іншу плату:</p>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">Thermostat</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Update</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> celsius </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> adcSensor</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">ReadCelsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// knows the ADC</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">celsius </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">20.0f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> gpioHeater</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">On</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">        </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// knows the GPIO pin</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">else</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">celsius </span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">22.0f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> gpioHeater</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">Off</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">private</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    AdcSensor  adcSensor</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">     </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// concrete low-level classes inside a high-level policy</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    GpioHeater gpioHeater</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>Після інверсії інтерфейси належать політиці, а драйвери їх реалізують. Стрілка залежності вказує на політику:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">before:   Thermostat ───▶ AdcSensor, GpioHeater</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">after:    Thermostat ───▶ ITemperatureSensor, IHeater</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">                                ▲                ▲</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">                                │                │</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">                          AdcSensor         GpioHeater   (or a fake in the test)</span><br></div></code></pre></div></div>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">ITemperatureSensor</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">virtual</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">~</span><span class="token function" style="color:rgb(130, 170, 255)">ITemperatureSensor</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">default</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">virtual</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">ReadCelsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">IHeater</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">virtual</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">~</span><span class="token function" style="color:rgb(130, 170, 255)">IHeater</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">default</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">virtual</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">bool</span><span class="token plain"> on</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">Thermostat</span><span class="token plain">   </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// high-level policy: knows only the abstractions</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">ITemperatureSensor</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain"> sensorToUse</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> IHeater</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain"> heaterToUse</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">sensor</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">sensorToUse</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">heater</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">heaterToUse</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Update</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> celsius </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> sensor</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">ReadCelsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">celsius </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">20.0f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> heater</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">Set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token boolean" style="color:rgb(255, 88, 116)">true</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">else</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">celsius </span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">22.0f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> heater</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">Set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token boolean" style="color:rgb(255, 88, 116)">false</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">private</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    ITemperatureSensor</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain"> sensor</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    IHeater</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">            heater</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">AdcSensor</span><span class="token plain">  </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token base-clause keyword" style="font-style:italic">public</span><span class="token base-clause"> </span><span class="token base-clause class-name" style="color:rgb(255, 203, 107)">ITemperatureSensor</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">ReadCelsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">override</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">GpioHeater</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token base-clause keyword" style="font-style:italic">public</span><span class="token base-clause"> </span><span class="token base-clause class-name" style="color:rgb(255, 203, 107)">IHeater</span><span class="token plain">            </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain">  </span><span class="token function" style="color:rgb(130, 170, 255)">Set</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">bool</span><span class="token plain"> on</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">override</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="на-c-9">На C<a href="https://embedbits.cz/uk/blog/solid-principles-c-cpp#%D0%BD%D0%B0-c-9" class="hash-link" aria-label="Пряме посилання на На C" title="Пряме посилання на На C" translate="no">​</a></h3>
<p>C пропонує два способи, і другий унікальний для C та дешевший за будь-який віртуальний виклик.</p>
<p><strong>1. Ін'єкція під час виконання</strong> через вказівники на функції, точнісінько як у прикладі про відкритість/закритість. Інтерфейс належить термостату (він у <code>Thermostat.h</code>), драйвери його заповнюють:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Thermostat.h</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifndef</span><span class="token macro property"> </span><span class="token macro property expression">THERMOSTAT_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">THERMOSTAT_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdbool.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* The thermostat owns the interface, the drivers implement it. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">struct</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">Get_Celsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">context</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">Set_Heater</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">context</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> bool on</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain">   </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">context</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token class-name" style="color:rgb(255, 203, 107)">thermostatPorts_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Update</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">thermostatPorts_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">ports</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><br></div></code></pre></div></div>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Thermostat.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Thermostat.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Update</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">thermostatPorts_t</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">ports</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> celsius </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> ports</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token function" style="color:rgb(130, 170, 255)">Get_Celsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">ports</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">context</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">celsius </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">20.0f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> ports</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token function" style="color:rgb(130, 170, 255)">Set_Heater</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">ports</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">context</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> true</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">else</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">celsius </span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">22.0f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> ports</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token function" style="color:rgb(130, 170, 255)">Set_Heater</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">ports</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">context</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> false</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>У модульному тесті ви передаєте функції, що повертають число й запам'ятовують стан нагрівача. На цільовій платформі ви передаєте <code>Adc_Get_Celsius</code> та <code>Gpio_Set_Heater</code>, а <code>Thermostat.c</code> лишається недоторканим.</p>
<p><strong>2. Ін'єкція під час лінкування.</strong> Іноді реалізації не потрібно перемикати під час виконання, достатньо ухвалити рішення один раз, у системі збірки. Тоді абстракція - це <strong>заголовок з оголошеннями</strong>, а реалізація - вихідний файл, який CMake обирає для цілі:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">Bsp_Thermostat.h</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">ifndef</span><span class="token macro property"> </span><span class="token macro property expression">BSP_THERMOSTAT_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">define</span><span class="token macro property"> </span><span class="token macro property macro-name">BSP_THERMOSTAT_H</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">&lt;stdbool.h&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* Implemented by the BSP of the board - or by a fake in the unit test. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Get_Celsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain">  </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Set_Heater</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">bool on</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">endif</span><br></div></code></pre></div></div>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">ThermostatLink.c</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"Bsp_Thermostat.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Thermostat_Update</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">float</span><span class="token plain"> celsius </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Get_Celsius</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">celsius </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">20.0f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Set_Heater</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">true</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">else</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">celsius </span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">22.0f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Set_Heater</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">false</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Для плати лінкується <code>Bsp_Thermostat_Stm32.c</code>, що реалізує обидві функції зі справжньою периферією. У модульному тесті на PC натомість лінкується <code>Bsp_Thermostat_Fake.c</code>. Жодних вказівників на функції, жодної непрямості, жодної RAM, а термостат і далі повністю незалежний від hardware. Це, до речі, саме те, що робить BSP від Embedbits: застосунок викликає інтерфейс BSP і не переймається, яка родина чи яка плата за ним стоїть.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ті-самі-ідеї-в-двох-мовах">Ті самі ідеї в двох мовах<a href="https://embedbits.cz/uk/blog/solid-principles-c-cpp#%D1%82%D1%96-%D1%81%D0%B0%D0%BC%D1%96-%D1%96%D0%B4%D0%B5%D1%97-%D0%B2-%D0%B4%D0%B2%D0%BE%D1%85-%D0%BC%D0%BE%D0%B2%D0%B0%D1%85" class="hash-link" aria-label="Пряме посилання на Ті самі ідеї в двох мовах" title="Пряме посилання на Ті самі ідеї в двох мовах" translate="no">​</a></h2>



































<table><thead><tr><th>Принцип</th><th>C++</th><th>C</th></tr></thead><tbody><tr><td><strong>S</strong></td><td>клас</td><td>модуль (пара <code>.h</code>/<code>.c</code>, префікс, <code>static</code>)</td></tr><tr><td><strong>O</strong></td><td>абстрактний клас, <code>virtual</code>, <code>override</code></td><td><code>const</code> таблиця вказівників на функції + <code>void *context</code></td></tr><tr><td><strong>L</strong></td><td>контракт базового класу</td><td>контракт, записаний у заголовку, без <code>NULL</code> у таблиці</td></tr><tr><td><strong>I</strong></td><td>кілька малих інтерфейсів</td><td>малі структури вказівників, розділені заголовки</td></tr><tr><td><strong>D</strong></td><td>ін'єкція інтерфейсу через конструктор</td><td>вказівники на функції в <code>struct</code> або ін'єкція під час лінкування</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="чого-компілятор-за-вас-не-зробить">Чого компілятор за вас не зробить<a href="https://embedbits.cz/uk/blog/solid-principles-c-cpp#%D1%87%D0%BE%D0%B3%D0%BE-%D0%BA%D0%BE%D0%BC%D0%BF%D1%96%D0%BB%D1%8F%D1%82%D0%BE%D1%80-%D0%B7%D0%B0-%D0%B2%D0%B0%D1%81-%D0%BD%D0%B5-%D0%B7%D1%80%D0%BE%D0%B1%D0%B8%D1%82%D1%8C" class="hash-link" aria-label="Пряме посилання на Чого компілятор за вас не зробить" title="Пряме посилання на Чого компілятор за вас не зробить" translate="no">​</a></h2>
<p>Треба бути чесним. C++ має переваги, які в C доводиться замінювати дисципліною:</p>
<ul>
<li class=""><strong>Немає перевірки під час компіляції.</strong> Якщо забути поле таблиці функцій, компілятор мовчить (designated initializers принаймні називають поля, а в C пропущене стає <code>NULL</code>). Використовуйте ініціалізатори <code>.Process = ...</code>, ніколи позиційні, і майте модульний тест, що викликає кожну функцію кожної реалізації.</li>
<li class=""><strong><code>void *context</code> не є типобезпечним.</strong> Неправильне приведення компілятор не знайде. Тримайте приведення в одному рядку на початку кожної функції (<code>movingAverage_t *self = context;</code>) і ніде більше.</li>
<li class=""><strong>Вартість.</strong> Непрямий виклик - це кілька тактів, а таблиці займають flash. На Cortex-M це зазвичай неістотно, але не в перериванні, що виконується кожні 10 мікросекунд. Там варіант із лінкуванням безкоштовний.</li>
</ul>
<p>І найбільша пастка: <strong>SOLID - це набір настанов, а не закон</strong>. Розбити функцію на 30 рядків на п'ять модулів із таблицями вказівників на функції, бо «так каже принцип», - це свій різновид спагеті. Застосовуйте принцип, коли є справжня причина для змін, друга реалізація або модульний тест, який неможливо написати без нього. Не раніше.</p>
<p>Якщо ви запам'ятаєте з цієї статті одне речення, нехай це буде таке: <strong>принципи стосуються напрямку залежностей і розміру частин, а не класів.</strong> C++ дає вам класи, C дає модулі й вказівники на функції. Для хорошого дизайну не потрібне ні те, ні інше, щоб бути вигадливим.</p>]]></content:encoded>
            <category>Вбудовані системи</category>
            <category>Архітектура</category>
            <category>C</category>
            <category>C++</category>
        </item>
        <item>
            <title><![CDATA[Дизайн і архітектура]]></title>
            <link>https://embedbits.cz/uk/blog/design-architecture</link>
            <guid>https://embedbits.cz/uk/blog/design-architecture</guid>
            <pubDate>Sun, 29 Sep 2024 20:59:52 GMT</pubDate>
            <description><![CDATA[OCD. Три чудові літери, які змушують мене постійно думати про архітектуру програмного забезпечення. Це може бути справді болісно, особливо коли доводиться мати справу з архітектурою в стилі «Arduino». Ви напевно її знаєте: весь проєкт у кількох папках, застосунок у папці src, низькорівнева функціональність у папці driver і так далі. Але що робити в складних системах?]]></description>
            <content:encoded><![CDATA[<p>OCD. Три чудові літери, які змушують мене постійно думати про архітектуру програмного забезпечення. Це може бути справді болісно, особливо коли доводиться мати справу з архітектурою в стилі «Arduino». Ви напевно її знаєте: весь проєкт у кількох папках, застосунок у папці <em>src</em>, низькорівнева функціональність у папці <em>driver</em> і так далі. Але що робити в складних системах?</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="модульність">Модульність<a href="https://embedbits.cz/uk/blog/design-architecture#%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C%D0%BD%D1%96%D1%81%D1%82%D1%8C" class="hash-link" aria-label="Пряме посилання на Модульність" title="Пряме посилання на Модульність" translate="no">​</a></h2>
<p>Кожна група функціональності має бути інкапсульована в пакеті. З'єднуючи такі пакети, ми отримуємо дедалі складніші системи. Уявіть проєкт, у якому потрібно виміряти температуру датчиком, підключеним до ADC, і надіслати актуальні дані через UART. Нам потрібен модуль, який читає сирі дані з ADC, обчислює значення в °C та надає його на своєму інтерфейсі, і ще один модуль, який відповідає за зв'язок через UART.</p>
<p>Якби ми об'єднали всю цю функціональність в одному місці, ми б одночасно визначали положення та швидкість елементарної частинки. Це спричинило б колапс хвильової функції та знищення всесвіту (сарказм).</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="шари-абстракції">Шари абстракції<a href="https://embedbits.cz/uk/blog/design-architecture#%D1%88%D0%B0%D1%80%D0%B8-%D0%B0%D0%B1%D1%81%D1%82%D1%80%D0%B0%D0%BA%D1%86%D1%96%D1%97" class="hash-link" aria-label="Пряме посилання на Шари абстракції" title="Пряме посилання на Шари абстракції" translate="no">​</a></h2>
<p>Уявіть дерево. Верхівка дерева — це єдина точка, яка обробляє або виконує потрібну функціональність. У вбудованих системах це може бути нескінченний цикл у функції main або якийсь обробник RTOS. Чим нижче спускається дерево, тим ширшим воно стає. Це відображає зв'язки та ієрархію між модулями. Коріння дерева — це зв'язки з апаратною частиною.</p>
<p>Базовий приклад — «blinky»: світлодіод, підключений до PA1, має світитися, якщо натиснуто кнопку, підключену до PA0, і навпаки.</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token macro property directive-hash">#</span><span class="token macro property directive keyword" style="font-style:italic">include</span><span class="token macro property"> </span><span class="token macro property string" style="color:rgb(195, 232, 141)">"stm32g4xx_ll_gpio.h"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">main</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token keyword" style="font-style:italic">while</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">!=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">LL_GPIO_IsInputPinSet</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">GPIOA</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> LL_GPIO_PIN_0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token function" style="color:rgb(130, 170, 255)">LL_GPIO_SetOutputPin</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">GPIOA</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> LL_GPIO_PIN_1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">else</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      </span><span class="token function" style="color:rgb(130, 170, 255)">LL_GPIO_ResetOutputPin</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">GPIOA</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> LL_GPIO_PIN_1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>У цьому прикладі ми змішали всі шари. Для примітивного проєкту це може бути прийнятно, але в складній системі це веде до анархії, безладу, спагеті-коду <strong>і кінця світу</strong>. Щоб уникнути жалюгідного, довгого та вирішального армагеддону, люди винайшли кілька чудових речей, як-от ієрархію та шари абстракції. У своїх проєктах я розділяю функціональність на три базові шари: Application, Middleware та Board Support Package (він же BSP). Вони, у свою чергу, поділяються на кілька шарів для забезпечення переносимості та модульності.</p>
<p><img decoding="async" loading="lazy" alt="Структура папок проєкту: Application, Artifacts, Bsp (Hal, LinkerFiles, Mcal, Ral, Startup), Docs і Middlewares" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUkAAAIWCAMAAADtZI8lAAAAtFBMVEUAN9oBPc8CQsQDSLkETq4FU6QGWZkHXo0IZIQJankKb20LdWIMDAwMfFcNJQwNgkkOOQwPSAwPijoQVQwRYQwRawwRdAwRlCcSfgwShgwTHTIToQ4UkAwUmQwUoQwVqwwVtAwWvQwWxgwZKk8dNGUhISEhO3cjQoUmSJIpTp8rUqstWLYvXMAxYcszZdU1a+E3b+o5dPU7eP9FRUVUVFRubm6Dg4OMjIyUlJSdnZ3CwsLMzMzWqPBxAAAaxklEQVR42u2dCVfbypaFS7M8CCUmiQAFAk4uIQ5hcKe73+v3//9X13iqbE0eEyztWutyhVQy8KUm1dY5m01RDlPY2/p1Ft93qbPJXbv9rLdP8mZee/r78qHzVqqzWG5x124/65gk39/M55/PxNH5nJfzujrnF+80rvn8TlXekOTTS+3p+8XXmjpEUp35uXzd6i8Rdz0tl8vXx6va6z9eGi9VP4t/zpYk393dXlze3cjDi4vP9SQv9OmbuahysznJ++WP+n64vK+pY0jqMy9PTq3uIu96Wi4Wj8unWpDLF37pebMPe+BVtyR5eXf27v3H+Xvd+LpI8i+f55uTfHy96iRp6xiS6sy35ffXn1v8JfKuJ/ERj8u6668vV+JHPGz8D8OmN18+3s1vBJsPt7ozmgN76eOt6tS3l+/v5oaUJHkj2pz8cvGF9+ZbzlEVQ/Ku9pPFpU/yc37/3//q3+br8qf4lR5fXx9eX79On54fXpdP3/ifo4qtM716XC5/LtwzvA1dPb7ojiYrP4mmJr4sly/fnvl/vOm8LJe6w6q7FEkxKtAlfXC/XMhaj/ZzKnXoNzQk5ze8D37hf+X89uJadEY6WLl0eccP5ufXdx8u559qSH6af7m4uPjIR8mb+Wd+pHv33cf6T+Z3Xc8F3P/+z781yZ/Lr+JXevq+/LXgf8jT8kn2r/vF0/JxsVjYOvLS08vSOSP+1gdx9MBr/hK9kgiI2x9/8lPfeYf9+fq88jm8d78+TO0lc/BddQPe8+lzKnXoNySS71QflAfiy+qBuPTlVrTL+ft38/Mvl9Oz+UUNyWvT553eLWac8/pP5pPX/FpW+69/1G1Xr4/yV1pMl4t7SfKr7nq2d6s6V3JoUyTVGX5qwVvQD12Jt2inLYl/FtH8nkWbfVh+s3epGYd/OF0yB4sqyUod5zfUJOUfz9vbjaZAB/aSKuccnaBYS/LD3fzL5YfKOPlp/rH2k3ljv3u/NsZ/WyNpBkNL0tYRzcqe4e2EN9vXXxLk8+u36SpJdbseJe7tXfJH/OCDIV0yBw9VkpU6zm+4Ocnrc1HO3vM2+Vm0yfO5YPbBkPwivpx9/HwrhgI9/plphV+r+2Re1mb1l6dpA8kfRNLWMSTVGd4yzFiqQSoCzyskf92LcmXvkj9CVKJL5kD9iCszTorPqdRpJFnfB+WlL3d6VTg/5wPf5/nFO9kzr0Udzu6MoFyKLn7OmyGRPLu9afjka/mvQb37Yfm9ieS9mUJ1na/LX6Z36zNT2Ry/82+unjW3p2czDBiSzzTtm7vkj7jiWOkSHdDcTZ9TrdNEsmFeEJfO+Xh3cXE5nd5dvBNz9wXHcMNnlmsxjF5f3NzcfppeXIr5RczUZ3d3/Pi9nHEubnnl+k/m1dwZ5/ll2kSSD3w/F4tvVIdf+qVnHH3mm2LNmxCfWhZyenoUdZ5efqgJ66v86Fc+BPycup/Dq77wn0WX6GBh1pPmc2rq0G+o1pOEq2GtIi+dX/Pe+Jlzuz07k6Pb2eXd/O6S1zm7nt+d887KkfL55VpePBfrofeqC99dtqyCLu0qyLS7OpLT+2fec7/Ztnn1S6+CzJmFnsBf7CqI13m9598taHCc3vPbOGz7ObKyXIaaS/bAPOOYz6mpQ7/hLs848y8fzz8c40n81+vVDnU2uWu3nyVa+OKYz928Uc3nA9kFen35etS9oPfn7wey4/hgHqxOcX/ybZWviy36NytNAbg99ydBEiRBEiRRQBIkQRIkUUASJEESpZ5kXoDkQUiOWQSSByGZeQlIYpwESZBEAcljk+T//f7P/7RXKo9fQBIkQfKtkcwYYyF9M+pG1lBnOCQT5tUSmKWpTyQ9ljXgS2cddYZD0g+bKJUhkRxFRX2V1N7cUGcwJHM2kY+U7SQbS9r4zzA4kiNWxL6gFow9FkycAyIpRky1Os0ixvyU93zGvCgTHFVx60x85sWF/pww7w3Jf/2uKw5JTmvM+FgXMj9JPG9mD4gkHzFDppB6SRqwsTgT+7w1ZvxCnKapU2fCUUdiqgpZmMYs6A3J+mJJFizlLWwk/vBM7MKN7IHbu1NJKWK54KlOFSxZ7d2qTqj+WWbqIGaDaZMT0aa8SPzxggPnSgdVkp75vhiFYSjXSBWSdFZ+TsoGM07GZqDbimTAe3kqmY1AUpMUzZE3zInq1BNJUh8Yuuu9W4yXE9G7Q3k4buzdgyKZKxAs5n94wCcaSUAfiEU7nzTycixmkzTNytwTY0HMB9YwmwSBz8fSwhPN06njzDiDIplKYmXo8z987DNfrILMAcckFj2in8rCW2keefwMn5B4jXEklz1ZwC/lTh1aBQ2sd9tVOFs/wA4GSIIkSIKkLEfaDZ72p4AkSIIkSO6uUJTlLOZr9SgHyemeCsXMY1EaswQkp3sqFKF8bp+hd29IslGhmLHYkNOP2xmLPW/sebNBktxdociY0bjNFlDGwgnv8CwdJMndFYqMhk+zLZnxuizNBkpyd4UikxvC7lb5sEnurlDM5HvsILkpyRaFIjTiA3r3ngpFzqfwNI1GzowDko2lVaGYxT5jwdhZBYHkBs/d2+xhgiRIgiRIDpEkdByQBEmQBMkGkjuoDyBZS3IH9QEk60juoj6AZB3JXdQHqjMkkkdRH6jOkEgeRX2gOmiTluRO6gPVwThpSe6kPoBkDcmd1AeqA5JUdlMfqA5IUtlNfaA6ILnn/iTVAUmQBEmQxP4k9idBEiRBEiShPkB9eGMkoT78BfWhZMzPA/6fTEzA+JkhkTyk+lCKXExxwgJ5JvGCQZE8pPogtntT+YAT+DKheo42aUluoz7YXEw65VqGcdKS3EZ9cEhGmSgFSFqS26gPlmTgFZi7V8t26oPM/jdTiQH5oJCApC1bqQ8qI2VmklVy/CC57/4kVuYgCZIgif1JRJGAJEiCJEgako3qg04NH4LkdE/1IWWpKGOQnO6pPqQMvXsbks3eEERSpj4XX/pj97ADyT28IWpI9sbuYQeSe3hDqHFytkKyL3YPR2iTLd4QtInmkOxLGvMjjJMt3hCrvTsAyXaSLeqDJRloJwiQbC5t6gORjFmUhqE/AsmW0qY+EMkiYl4W8iEAJDd57kZuFpAESZAEyXaSeDsaJEFy4CRX7adB0iXZqD4wlpQ5o7co6+ynQdIl2ag+CKe70TrJVftpkHRINqsPLGGzMFojiXGykWSz+sDSIGVjuROsfKit/XTfXKePrD6wdOT54m1z40Nt7af75jp9ZPWBpTlLzHv7hY4VcwxqY4Y2uZn6IBkKktaH2pIclJvqdE/1gUhaH2qQbCbZoj4Yko4PNUg2ljb1wZC0PtRkPw2SldKmPlDvJh9qsp8GyQPtT2JlDpIgCZLYn4SOA5IgORSSjuiAt1maSDaqD3IdrhMvOaIDSDaRbFQfxINhLDYqV0UHkGwg2ZZ5ST4tJtWQCJCsI9msPujttdCKDkXMWJLiPfNt1QciuSI6hD5D7MOW6oOkN1G9W25Lqj1KH21yW/VBzjhMzTgp0ztsYq7HOFk3TraoD2IV5OlVEEjul3lpLYZRZbXyQbKutKkPRNIRHaLhzjh7qA/OPpsWHYpowKsgvB0NkiAJkiB5IJLY6QVJkARJkGwg2ez7kPqMRRlIbkqyUX0Ys0CoDwVITvdUHyLGIRYztMkNSTarDzHLdduUThpiyzcNmIjSGSTJPdSHjHky5Xbpia3yEec6Emm50zHUhy3VhzIPGUsKMSdxoJEn+ns+1N79z+/fu6sPYpc8ZIHu2MLjZcK7fTIZJMnO0qY+yJKIZsjbo9hW5/PPOPaHmVetm2SL+lAokvK7cUyLzqQPZi6HJ9mmPvhBIrTFUq45fWHglCby/Ra0yZrSpj4kAWmLKZPEY602gmTbc3f7/mTsFcNemR+K5Hg1yhskdyOZxYFYC4Hk3iRTLxwN+7m7PHSZ9rCAJEj2kSSFM2R83RiBZDvJirKQpbP1HEo585I6S4KVyoMnWVEWUvu9CWcwG7p1dgUZSCqSVWUhrWo2YVCCZBdJoywYt2jt08KcHEo6W3lp0ytJL0XfJIaXtfonOqyR3FhZMG7RysQzTW0OpSxNfV96ElB6pUyMmwEb28o9FB3WSG6sLJBbtNthU2bdhPRZmV5JSQyZ07t7KDps2SZJWbAA10na3k3plbxwfZzsoeiw5TgZ24HOABw1kqT0SpYkVe6f6LAlSVIWLMmMjetJ2vRKEa2KnMp9Ex22I0nKgnWLLgtPNL3chjMYkja9Uu6JQSF2KvdQdNiOJCkLjlu0fDTkjY7CGah3U3qlMo88seaxlXsoOmAHAyRBEiRB8pAkQRskQRIkD1VOMMz2cCSdJXqT6LCJMAGSMtlFqh4bW/bVO7fTQbLuLSCQ3I9kIJKwRMxRKIxm0SpM8Md1Fhenm5TpcLEPoTFEnnlhOWKj0ooORrNoEyYmHGziBaeblOlwsQ8h7RNNWCw3NN0N9pReuGoQJgJfvhOYn2xSpkO2Sfu2KfOLsk6qaBEmjAx0sqmEjjLjJJrkaJ1kizDBokyUAiQdkhOWqNeDSHTQJNuEiUC/NX2ySZmOsJ40Mw6JDqRZtAkTfA7iZ5LyZJMyHeEZx6yCSHSwmkWLMCHXRSJ87FSTMmEHAyRBEiRB8lRJntq/CEiC5OBIpuyIlMQLxeHKQR9I1lpLK5Iy43FctFOZxR6ZQ2ykUJTyhWI/XDnoB8kaa2lDUmThDttBevwpMWbJ5vvqKy9tOgenT7LeWjo1mTHi9m4eqlxCJUhaa+ly4qu+bHQETdLTf638ooMgbJMUj9x6K0nfbgypa1QMqlND0qgYpvLp7fSStbTQESLRl42OoF1dxg5JEwThzB0j2pNzbheG1DUqBtWpkiQVw1Q+PfWBrKVDlchqRjqCnnEyh2S0Hk6WUQc2t5MhdY2KQXWqJI2KQZVPsU1qGwIJMHV0BNW7R3wgJJImCMIhaQZYczsZrdaoGFSnStIGs+jKpzhOtpMsg1D9tUFogyCccTJqJjnahqRWMfpA0nQ90hEUk4Iv+ERkY0HLocTt4qFRKpzebQ03VlWMld69lisioNwvp0/SmTIiO+OkPr8UizOhP6IgCFtyT0xC0ci9nex01lQMd8ZJxLyU2wOjYlDlEyZpV0ERrYLElJPIM17Gv6MgiJVnHL58CcbOKohIVlQMZxUkfoifOQdaxaDK2MHADgZIgiRIgmS/AkxAEiSHQTIbtYsFLXVAsslaulYsaKkDkk3W0k1b3E11QLLJWrqRJMbJVpIr1tLk+0CKAJFcrRPmts4ASG72nvmqtbT2fSBFwOa5cu2nhcRAdQZAsjv2oVy1lra+D1YRcHq3qaMlBlOn9yS7fR8q1tLWrcBJb1UlqQ9Nnd6T7CxVa2mHJCkCLSR1HZCsWktb3werCDi11kiaOiBZay2tfR8oroE0AqeOJmnqgGSNtTT5PlBcA2kETh2SvVQdkKx91Qc7GCAJkiA5EJIDy0YFkiAJkiC5Pcnm4AUQ3JZkU/ACCG5Lsil4AQR3Iek5MQtGWQDBVZKbZFWSwQsUs2CUBRBcJblJViUZvEAxC0ZZAMFt26QOXqCYBbMfDoI7jJMyaMHELIDkHiQL8yZKIlPrK2UBBLdfT8rgBYpZMMoCCG7/jCODFyhmwSgLILjvc7fZewRBkARJkARJVbCHCZIgCZLVSAeQ3I6kpJY5AQ0U6QCSe5KktyVBEiT/Mklr7gCStSQ7s9wIz+lYkDTmDiBZT7Iz81LFdRokd2yTpneTuQNI7jlOkrkDSO5H0po7mH0OkNyNpDV3MJEOILlj77bmDjrSASSxgwGSIAmSIHlaOaxAEiRB8u+TXMuqtGGwmLzrDTg4/G2SLq61rEotJHXa95DuegMODm+J5FpWpVaS0ltw7N4VgmSzp0O5jekLSDrq7GpWpZCSkq+bMliSdFfVwcEEXPSF5MYOv9WsSvLbiBVVUwZLku6qOjiYgIu+kNwo81J9ViVxKRd54iumDHqcnK309HUHBxNwMbg2Wc1gEzKRkymvMWUonRzl6ySpsgm4GOKMUyGpFzoVU4aVGWedJFXWARcgKQ8iIUpUTBnqSa47OOiAi+GQ1K7T1axK4qDwOJiKKYMlSXdVHBwo4GI4JCmZ0npWJb0KSqqmDJYk3VVxcKCAC+xgYAcDJEESJEFyQ5LTPheQBMkhkhxGEMTRXacHEwRxdNfpwby6f3TXaZDcniS5TovnZT8tBxYEcQTX6Yx5SRqw8cCCII7gOq3EAi14DycI4giu054RWYcVBHEEX1oiOawgiCOQNFLgwIIgjkAy91jM55pyYEEQx3CdziNP5qgcVhAEdjBAEiRBEiT/PsnpIApIguSASCLz0q4rc8d1GpmX9iLpp2miXaeRL2gvkmGpXGhB8hAkxXM3Mi91kNwoy82Et0lkXuog2ak+qHGyQOalvdukKH5eIvPSIcbJWETgIPPSAUhmLC6Reekgc3fIZsi8dKBVUIzMS9jBAEmQBEmQ/FskhyHxgCRIgiRIdpJMfcbkvmSWzhoDSpougaQlOWaBiGd3MjPUpgfKQLKLpMhiUxazEiT3JRnrF1B1pjTmxD6YPEv2ktxuE1/64159uJ3ejHlyEFQ5LtK0tDKEybNkL1mSvXGvPpz6UOYhY0mx1oWVDGHyLNElh2Rf3Ks3JfnP798dbVI0x1Dk/iJcVoYgtjUk++K5vCnJzmJtlFUuupXYByI5ckkGIFlLslAkRXYaNl6LfSCS+hKfaPSl/rhXH46kHyQie1opkyzxxpiTDGHzLJlLfKKP0jD0Rz1yrz4cySTQhtS85Yl8kTnJEG4WOn2piJjHR1XWI/fqA4+TW5f++NuCJEiC5KBJDj5CBCRBEiRBcpO4hmAMkgcg6aeJLxK9guS+JEOZJD8DyYOQnIlGOfGZJ70YTHql0zBl+GMkN4prKP1QejFE6u1dnV7pNEwZ/hjJzrgG89a4tHIQXyi90mmYMryxNhmEJe3bUlKg0zBleFvjZMGiGpKnYcrwtkjGfFRc690nY8rwltaTqS8MbmjGMemVTsSU4W0948goWVoFmfRKp2HKgOdukARJkATJ/pDE29EgCZInT9JxlCYTMH7Q4hYNku7K3Ho6OI7SLskWt2iQdElaTwfHUdol2eJxDJIOyXpPB5DcnqTxdCBv6II/bycCIB1U3KJHLBeJmHifrwRBnJqj9OYkN/Z0WHGUDn3mHFTcojOWjb2kZHE1COLUHKU3J7mxp4PpyeqlZw6QDqpu0TmbpPyUHhNWgyBOzFF6U5LdcQ02V7kmqY4TZg9q3KJZGk14S85qgiBOzFF6U5KdZUuSxi2apeHMy3ijqwZBnJij9BFJzsQGuujUdFDjFh3GrAwl7UoQxIk5Sh+BpOMoHZkZJzIzzrpbdBgEZcxHxJogiBNzlD4CSfKGFjmV1CooolXQult0ytur7NQ1QRCn5Sh9OJLYwQBJkARJkOwqh90NnvawgCRIDomkif8Cye1IVuMjQHJHkpX4CJDckWQlPgIk9yAp4yOMshAykVUpczKxCKki5g/pwya5VXyEVBZMjgs3p42SKoZNcuP4CFIW5KvUcgdJkySpAm1ys/gIoyyYXEBEkjbYMU52jpMyPsIoC5JkbkgGILkNSREfYdUHVqrIJ5NniaQKkOxYT6r4CKs+MHEmKd08SxFmnC3iI1xlIZwoWULlWTJSBUgeZAcjAsnDkCy8ACT3JjkWkiybgOTeJFPGfPHSGkjuRnI6iAKSIAmSINlFclj7uiAJksMkafIS9Flr2Idk104vkaRcGX3WGvYh2aU+EEmTv6XXWsMfaZPmhfJe75D/kXESJA9F0vTuXmsNxyWZipK5M06EGWcnkjoswq6Ceqw1/IXn7ggkD0Oyr1rDnyXZZ63hz5Lss9bwF8bJwbxwDpIgOTiSKQPJLUm62ZnWSIpFu8qkCpJbZmeqkOTrodpcYSBZR7I5O5Pc3IgZSG5I0mRnWlEfEkvSc4IgeugWccD3zE12por6oHq3N3ZI9tAt4oCxDyY7U0V90DOOG5jTQ7eIQ7ZJnXWksmeuevdIpOg3JHvoFnHIcbKdpAh+ILPpHrpFHIFkRX1QJAs/tGbTPXSLOALJivqghAmfX6IgiB66RRyBZEV9UMKElzhBED10i8AOBkiCJEiC5FskOe1hAUmQBEmQ7H4vaGTz8K6WijABku0kg2aSa8IESLaS9FjeRHJdmADJVpJhGAuSuUymL3d9Y88be96MhAlyghg2yc73zMOxJ9ukzBeUCGjhhEXCn8AIE+QEMWySnbEPYemNBcmR6OF+oHZ9U/VVCRPkBIE22d4myzgSJAsW8849WiFpttO1EwTGyfZxkvOLRXuMPN65Z/UklRMESHaQLH1pMjZmEz8q60gaJwiQ7CI5Ul4EXsDG9SS1EwRIdpEsFMmYeWUtSeMEAZJ47gZJkARJkDwgyemwCkiCJEiCZCfJ1GcsyspU544OQXJHkmMWiNfPikzGOaTpGCR3JBkx8XrarCSPS5DckWSsXx0HyS6SXTu9GfOkvSdIdpHsUh/KPGQsKUBy7zYpCErNGyQPsZ5M5GAJknuRLBTJDCT3JekHSao2y0FyP5JJoMIZQBLP3SAJkiAJkn+Y5MA8vkESJAdA0k0Rn422zcUEkiQ6rJD0at47XyEpA5ajvJZcZnbphkWSRIcVkqOo6CIpQ+Zn9YnEsoH2bvWQ2GIAUUtS3piAZC3JscfCXHzPi47HyQP+n83FtDaoSn9Q/tSeOZXNcFGOxcuY/HrSF5LdO71EMuQdPeB/fJoqUPx/cZzwU5SLqYYk7+W+aISmciYO0pR/pBfKJDl5X0h2qg+W5EymjHZ6sggl4edtLqZVkhPduwv5f13Z9u5EfqDXk979z+/fm7dJF6E74tkMQqszDuMzTjEKw9CkGlodJ2cCMIt7QrKz7ExS72oGXpKmqyRH5oC3x8mbz0H9R0naXEyVZXzGSemkTEQyk1ON7P1jkSlwuCTHYsZJ00zNHbPSycVUITljYTYJAn9kK5eFJ9qpWLX7oR8PmaRxglDrmax0cjFVHy3HPvPH/LKtzD9QJKnMVWb08ZBIbl4yRnYbm5VY+NSDZLXM0lRbwGxWxicgsf0dkts14DhQr3aA5J4l9cJROSCSgy8gCZJvrfw/ScJr6XVl0eYAAAAASUVORK5CYII=" width="329" height="534" class="img_ev3q"></p>
<p><em>Application</em> містить те, що робить продукт. <em>Middleware</em> містить повторно використовуване програмне забезпечення без залежності від апаратної частини, як-от логер, стек ModBus або менеджер енергонезалежної пам'яті. А що таке BSP? Просто. Він містить усе необхідне для зв'язку, конфігурації та керування апаратною частиною. У нашому прикладі вище операції з GPIO належать до BSP. Користувачеві не слід перейматися точним підключенням кнопки та світлодіода, тож інтерфейс між BSP і застосунком може виглядати як <em><strong>Bsp_Get_ButtonState()</strong></em> і <em><strong>Bsp_Set_LedState()</strong></em>. Якщо в апаратній частині щось зміниться, нікому не доведеться правити кілька місць, оновлюється лише BSP.</p>
<p>Отже, BSP містить принаймні один шар інкапсуляції для цих геттерів і сеттерів. Але що вони використовуватимуть? Чи писатимуть вони в регістри напряму? Не думаю. У STMicroelectronics є два набори «драйверів»: «HAL» і «LL». Чому лапки? Тому що жодна з назв не відповідає дійсності. Їхній пакет «HAL» насправді є величезним і повільним спагеті-кодом, який не має нічого спільного зі своїм описом, адже HAL розшифровується як Hardware Abstraction Layer. До справжнього ми дійдемо наприкінці.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="register-abstraction-layer-ral">Register Abstraction Layer (RAL)<a href="https://embedbits.cz/uk/blog/design-architecture#register-abstraction-layer-ral" class="hash-link" aria-label="Пряме посилання на Register Abstraction Layer (RAL)" title="Пряме посилання на Register Abstraction Layer (RAL)" translate="no">​</a></h2>
<p>Функції <code>LL_GPIO_xxx</code> з прикладу — це інкапсульований доступ до регістрів MCU. Операції з регістрами можна писати де завгодно, але що, якщо ви змінюєте MCU або припускаєтеся помилки? Доведеться виправляти її в кожному місці. Це було б болісно, тож ми щойно знайшли найнижчий шар абстракції — <em>Register Abstraction Layer</em>. Він представляє лише абстракцію регістрів, інкапсульовану у (зазвичай inline) функції зі змістовними назвами.</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">__STATIC_INLINE </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">LL_GPIO_SetOutputPin</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">GPIO_TypeDef </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">GPIOx</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">uint32_t</span><span class="token plain"> PinMask</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token function" style="color:rgb(130, 170, 255)">WRITE_REG</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">GPIOx</span><span class="token operator" style="color:rgb(137, 221, 255)">-&gt;</span><span class="token plain">BSRR</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> PinMask</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Назва «LL», на мій погляд, некоректна (як і їхній HAL, але хто б його використовував?), проте ми отримуємо доступ до всіх регістрів зі змістовним описом, тож можемо користуватися цим без складного ручного написання.</p>
<p>У моїх проєктах RAL складається з трьох частин:</p>
<ul>
<li class=""><strong>CMSIS</strong> від ARM: визначення ядра Cortex-M,</li>
<li class=""><strong>CMSIS_ST</strong> від STMicroelectronics: заголовки пристроїв з визначеннями регістрів та системною ініціалізацією сімейства MCU,</li>
<li class=""><strong>RAL_ST</strong>: оригінальні LL-драйвери ST разом з моїм шаром <code>Port</code>. Шар <code>Port</code> забезпечує уніфіковане іменування, що не залежить від сімейства MCU, тому код над RAL не змінюється, коли змінюється MCU.</li>
</ul>
<p>RAL знає регістри і більше нічого. Він не знає, що до PA1 щось підключено.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="microcontroller-abstraction-layer-mcal">Microcontroller Abstraction Layer (MCAL)<a href="https://embedbits.cz/uk/blog/design-architecture#microcontroller-abstraction-layer-mcal" class="hash-link" aria-label="Пряме посилання на Microcontroller Abstraction Layer (MCAL)" title="Пряме посилання на Microcontroller Abstraction Layer (MCAL)" translate="no">​</a></h2>
<p>Отже, ми маємо легкий доступ до регістрів. Але що далі? Доступ до GPIO тривіальний, а як щодо інших периферійних модулів? Для передавання через UART для кожної операції потрібно кілька регістрів: увімкнути периферію, встановити baudrate, довжину біта, парність, стоп-біти і так далі. І оскільки ми вже знаємо, що кожна функціональність, яка використовується більше одного разу, має бути інкапсульована, ми створюємо функції для зчитування помилок, запуску передавання, запису в регістр вихідних даних, зчитування регістра вхідних даних та багато інших.</p>
<p>Ця функціональність не може бути RAL, адже вона ієрархічно вища й викликає RAL. Вона опікується лише периферією мікроконтролера, тому цей шар називається <em>Microcontroller Abstraction Layer</em> (MCAL). Користувач бачить <code>Gpio_Set_PinLevel()</code> або <code>Usart_Send()</code> і не мусить знати жодного регістра. Одна периферія — один інтерфейс: якщо I2C потребує DMA, модуль I2C обробляє це внутрішньо.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="hardware-abstraction-layer-hal">Hardware Abstraction Layer (HAL)<a href="https://embedbits.cz/uk/blog/design-architecture#hardware-abstraction-layer-hal" class="hash-link" aria-label="Пряме посилання на Hardware Abstraction Layer (HAL)" title="Пряме посилання на Hardware Abstraction Layer (HAL)" translate="no">​</a></h2>
<p>Тепер ми можемо ініціалізувати всю периферію MCU: RCC, DMA, USART/UART і так далі. Але конфігурація має бути сумісною з пристроями, підключеними на нашій друкованій платі. Тож ми зосереджуємося не лише на MCU, а на всій схемі. Отже, нам потрібен ще один шар абстракції — <em>Hardware Abstraction Layer</em> (HAL). Так, це справжнє значення цієї назви, а не те, що є в «HAL» від ST.</p>
<p>HAL — єдина точка інтерфейсу між застосунком (або middleware) та апаратною частиною. Він знає, який пін є світлодіодом, який UART підключено до роз'єму налагодження і як налаштована периферія. Щоб забезпечити потрібну функціональність, він поєднує кілька модулів з MCAL. Це останній шар абстракції BSP.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="board-support-package">Board Support Package<a href="https://embedbits.cz/uk/blog/design-architecture#board-support-package" class="hash-link" aria-label="Пряме посилання на Board Support Package" title="Пряме посилання на Board Support Package" translate="no">​</a></h2>
<p>BSP — це апаратно-орієнтований пакет, який містить усе необхідне для ініціалізації та використання всієї периферії MCU і підключеної схеми. Окрім трьох шарів вище, він також містить код <strong>startup</strong> (ініціалізація секцій пам'яті та виклик застосунку) і генерацію <strong>linker</strong> script для вибраного MCU. Кожне сімейство STM32 має власну гілку, тому інтерфейс BSP залишається незмінним, а змінюється лише те, що під ним.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">Application / Middleware</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">────────────────────────</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">          HAL            what is connected to which pin, how the board is configured</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">          MCAL           peripherals: Gpio, Usart, I2c, Dma, ...</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">          RAL            registers: CMSIS, ST LL drivers, unified Port layer</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">────────────────────────</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">         Hardware</span><br></div></code></pre></div></div>
<p>Правило просте: <strong>шар викликає лише шар, розташований безпосередньо під ним.</strong> Застосунок ніколи не підключає заголовок ST, MCAL не знає, який пін є світлодіодом, а RAL не знає, яка периферія для чого використовується.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="знову-blinky">Знову blinky<a href="https://embedbits.cz/uk/blog/design-architecture#%D0%B7%D0%BD%D0%BE%D0%B2%D1%83-blinky" class="hash-link" aria-label="Пряме посилання на Знову blinky" title="Пряме посилання на Знову blinky" translate="no">​</a></h2>
<p>Перепишемо приклад. Застосунок не знає жодної апаратної частини:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">main</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Init</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token keyword" style="font-style:italic">while</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Set_LedState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Get_ButtonState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>BSP знає плату і використовує MCAL. Піни описані в єдиній таблиці, тож нова ревізія друкованої плати змінює один рядок і більше нічого (спрощено, обробку помилок пропущено):</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">typedef</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">struct</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PortId_t</span><span class="token plain"> portId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinId_t</span><span class="token plain">  pinId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain">   </span><span class="token class-name" style="color:rgb(255, 203, 107)">bspPin_t</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">bspPin_t</span><span class="token plain"> bspButton </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> GPIO_PORT_A</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> GPIO_PIN_0 </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">bspPin_t</span><span class="token plain"> bspLed    </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> GPIO_PORT_A</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> GPIO_PIN_1 </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">bool </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Get_ButtonState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token class-name" style="color:rgb(255, 203, 107)">gpio_PinLevel_t</span><span class="token plain"> pinLevel </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> GPIO_PIN_LEVEL_LOW</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token function" style="color:rgb(130, 170, 255)">Gpio_Get_PinLevel</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">bspButton</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">portId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> bspButton</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">pinId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">pinLevel</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">GPIO_PIN_LEVEL_HIGH </span><span class="token operator" style="color:rgb(137, 221, 255)">==</span><span class="token plain"> pinLevel</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Bsp_Set_LedState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">bool isActive</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token function" style="color:rgb(130, 170, 255)">Gpio_Set_PinLevel</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">bspLed</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">portId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> bspLed</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">pinId</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">                          isActive </span><span class="token operator" style="color:rgb(137, 221, 255)">?</span><span class="token plain"> GPIO_PIN_LEVEL_HIGH </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> GPIO_PIN_LEVEL_LOW</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>А десь унизу, усередині <code>Gpio_Set_PinLevel()</code>, функція RAL <code>LL_GPIO_SetOutputPin()</code> нарешті записує в регістр <code>BSRR</code>. Чотири шари заради блимання світлодіода виглядають безглуздо, і для blinky так воно і є. Але суть у тому, що відбувається, коли продукт росте:</p>
<ul>
<li class=""><strong>Нове сімейство MCU:</strong> вибирається інша гілка BSP, застосунок не чіпається.</li>
<li class=""><strong>Нова ревізія друкованої плати:</strong> світлодіод переїжджає на інший пін, змінюється один рядок у HAL.</li>
<li class=""><strong>Unit-тест застосунку:</strong> BSP замінюється фейковим, і застосунок запускається на ПК без жодної апаратної частини.</li>
<li class=""><strong>Помилка в доступі до регістра:</strong> вона лише в одному місці.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="де-це-знайти">Де це знайти<a href="https://embedbits.cz/uk/blog/design-architecture#%D0%B4%D0%B5-%D1%86%D0%B5-%D0%B7%D0%BD%D0%B0%D0%B9%D1%82%D0%B8" class="hash-link" aria-label="Пряме посилання на Де це знайти" title="Пряме посилання на Де це знайти" translate="no">​</a></h2>
<p>Усі описані тут шари мають відкритий код і документацію, кожен у власному репозиторії організації <a href="https://github.com/Embedbits" target="_blank" rel="noopener noreferrer" class="">Embedbits</a>:</p>
<ul>
<li class=""><a class="" href="https://embedbits.cz/uk/docs/bsp">BSP</a>: пакет зі startup, linker script і HAL,</li>
<li class=""><a class="" href="https://embedbits.cz/uk/docs/bsp/ral">RAL</a>: CMSIS, CMSIS_ST і RAL_ST,</li>
<li class=""><a class="" href="https://embedbits.cz/uk/docs/bsp/mcal">MCAL</a>: модулі периферії разом з таблицею підтримуваних сімейств STM32,</li>
<li class=""><a class="" href="https://embedbits.cz/uk/docs/platform/embi-platform/coding-style">стиль кодування</a>, який робить назви в цих шарах читабельними.</li>
</ul>
<p>Якщо хочете побачити ту саму ідею з погляду мови, прочитайте статтю про <a class="" href="https://embedbits.cz/uk/blog/solid-principles-c-cpp">принципи SOLID у C++ і C</a>. Шари вище — це не що інше, як <em>dependency inversion</em>, застосована до всієї прошивки.</p>]]></content:encoded>
            <category>Вбудовані системи</category>
            <category>Архітектура</category>
        </item>
        <item>
            <title><![CDATA[Скінченні автомати]]></title>
            <link>https://embedbits.cz/uk/blog/finite-state-machines</link>
            <guid>https://embedbits.cz/uk/blog/finite-state-machines</guid>
            <pubDate>Sun, 29 Sep 2024 16:24:40 GMT</pubDate>
            <description><![CDATA[Для розробки embedded-програмного забезпечення необхідний належний дизайн. Мікроконтролер має обробляти сам застосунок і спілкуватися з безліччю підключених схем через внутрішню або зовнішню периферію. Виконання застосунку має бути якомога швидшим. Але це справді жахливе пояснення для будь-кого. У реальному світі розробник має забезпечити оптимально короткий логічний шлях виконання коду. Це означає, що розробник не повинен перевіряти всі умови в кожному головному циклі. Один зі способів такої оптимізації — правильне вкладення умов. З ростом складності проєкту це може бути справді непросто. Велика кількість вкладених умовних операторів може призвести до нестабільності коду та погіршити його читабельність. Читабельність коду жорстоко необхідна для майбутніх оновлень або супроводу наявного коду. Одна з моїх улюблених цитат каже:]]></description>
            <content:encoded><![CDATA[<p>Для розробки embedded-програмного забезпечення необхідний належний дизайн. Мікроконтролер має обробляти сам застосунок і спілкуватися з безліччю підключених схем через внутрішню або зовнішню периферію. Виконання застосунку має бути якомога швидшим. Але це справді жахливе пояснення для будь-кого. У реальному світі розробник має забезпечити оптимально короткий логічний шлях виконання коду. Це означає, що розробник не повинен перевіряти всі умови в кожному головному циклі. Один зі способів такої оптимізації — правильне вкладення умов. З ростом складності проєкту це може бути справді непросто. Велика кількість вкладених умовних операторів може призвести до нестабільності коду та погіршити його читабельність. Читабельність коду жорстоко необхідна для майбутніх оновлень або супроводу наявного коду. Одна з моїх улюблених цитат каже:</p>
<!-- -->
<p><img decoding="async" loading="lazy" alt="Фрагмент таблиці підпрограм автомата станів на C" src="https://embedbits.cz/uk/assets/images/fsm-state-machine-cc3162be67be4421b1cac2bb42f51afd.png" width="551" height="586" class="img_ev3q"></p>
<blockquote>
<p><strong>Завжди пишіть код так, ніби той, хто в підсумку супроводжуватиме ваш код, — агресивний психопат, який знає, де ви живете.</strong></p>
</blockquote>
<p>Тож нам краще подумати, перш ніж писати власне код. Цей процес називається «дизайн», і він мені дуже подобається. Бо якщо робити його правильно, обсяг нового коду та подальшого редагування цього нового коду справді скорочується. Іноді може здаватися, що це забирає багато часу, але насправді цей час разом із часом написання коду коротший, ніж написання коду, який доведеться постійно переписувати. Але як це полегшити? Тут можна почати говорити про скінченні автомати (Finite State Machines), також відомі як FSM.</p>
<p>Тож почнімо з безглуздого опису, згенерованого ШІ:</p>
<blockquote>
<p>Автомати станів, зокрема автомати Мілі та Мура, є фундаментальними моделями в проєктуванні цифрових систем і теорії обчислень. Обидва ці автомати використовуються для представлення систем, які переходять між різними станами залежно від вхідних даних і в результаті видають вихідні дані. Проте саме спосіб формування виходу відрізняє їх одне від одного.</p>
<p><strong>Автомат Мілі</strong> формує вихід залежно і від <strong>поточного стану</strong>, і від <strong>поточного входу</strong>. Це робить автомати Мілі чутливішими, адже вихід може динамічно змінюватися з кожним вхідним сигналом.</p>
<p>З іншого боку, <strong>автомат Мура</strong> формує вихід виключно залежно від свого <strong>поточного стану</strong>, незалежно від входу. Це робить автомати Мура простішими та передбачуванішими, адже вихід залишається незмінним, доки не зміниться стан.</p>
<p>Ці автомати станів широко застосовуються у вбудованих системах, системах керування та цифрових схемах, де критичні точні переходи між станами та виходи. У цьому блозі ми розглянемо, як працюють автомати Мілі та Мура, їхні ключові відмінності та як їх можна застосувати в практичному проєктуванні систем.</p>
</blockquote>
<p>Але що це означає? Простими словами, скінченний автомат Мілі перевіряє умови для потрібного стану <em><strong>перед</strong></em> виконанням поточного стану. Автомат Мура перевіряє умови <em><strong>після</strong></em> виконання поточного стану.</p>
<p>Але що таке стан? Уявіть лампочку, яку можна вмикати й вимикати натисканням кнопки. Для обробки такої поведінки нашому автомату потрібні лише два стани. Один стан, що активує виконавчий пристрій, і другий стан, що цей пристрій деактивує. Справді просто, чи не так? А що в складних системах? Що ж, вам потрібна проста реалізація, придатна для різних ситуацій. Саме це робить шаблон нижче.</p>
<p>Під час роботи в автомобільній галузі ми з моїм колегою <a href="https://www.linkedin.com/in/jano-sima/" target="_blank" rel="noopener noreferrer" class="">Яном Сімою</a> зіткнулися з проблемами в дизайні скінченних автоматів. Було безліч різних стилів, але жоден не мав достатньої функціональності, щоб досягти стабільності та чистого дизайну. Тож ми вирішили розробити власний шаблон. З дозволу Яна я публікую шаблон нашого скінченного автомата під ліцензією MIT.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="завантажте-шаблон">Завантажте шаблон<a href="https://embedbits.cz/uk/blog/finite-state-machines#%D0%B7%D0%B0%D0%B2%D0%B0%D0%BD%D1%82%D0%B0%D0%B6%D1%82%D0%B5-%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD" class="hash-link" aria-label="Пряме посилання на Завантажте шаблон" title="Пряме посилання на Завантажте шаблон" translate="no">​</a></h2>
<p>Шаблон опубліковано під ліцензією MIT. Усе є звичайним текстом, тож ви можете прочитати його перед завантаженням:</p>

























<table><thead><tr><th>Файл</th><th>Що це</th></tr></thead><tbody><tr><td><a href="https://embedbits.cz/uk/downloads/fsm/FsmTemplate.c" target="_blank" rel="noopener noreferrer" class="">FsmTemplate.c</a></td><td>Шаблон на <strong>C</strong> (C99), один файл зі станами, таблицею підпрограм і заглушками</td></tr><tr><td><a href="https://embedbits.cz/uk/downloads/fsm/FsmTemplate.hpp" target="_blank" rel="noopener noreferrer" class="">FsmTemplate.hpp</a> і <a href="https://embedbits.cz/uk/downloads/fsm/FsmTemplate.cpp" target="_blank" rel="noopener noreferrer" class="">FsmTemplate.cpp</a></td><td>Шаблон на <strong>C++</strong> (C++17), клас з одним автоматом станів усередині</td></tr><tr><td><a href="https://embedbits.cz/uk/downloads/fsm/Fsm.hpp" target="_blank" rel="noopener noreferrer" class="">Fsm.hpp</a></td><td>Header-only рушій для версії на C++ (без heap, без винятків, без RTTI)</td></tr><tr><td><a href="https://embedbits.cz/uk/downloads/fsm/fsm-instantiate.sh" target="_blank" rel="noopener noreferrer" class="">fsm-instantiate.sh</a></td><td>Невеликий скрипт, що замінює назви, <code>fsm-instantiate.sh Button FsmTemplate.c src/</code> створює <code>src/Button.c</code></td></tr></tbody></table>
<p>Назви в кутових дужках замінюються назвою вашого модуля: <code>&lt;Module&gt;</code> стає <code>Button</code>, <code>&lt;module&gt;</code> стає <code>button</code>, а <code>&lt;MODULE&gt;</code> стає <code>BUTTON</code>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="як-це-працює">Як це працює<a href="https://embedbits.cz/uk/blog/finite-state-machines#%D1%8F%D0%BA-%D1%86%D0%B5-%D0%BF%D1%80%D0%B0%D1%86%D1%8E%D1%94" class="hash-link" aria-label="Пряме посилання на Як це працює" title="Пряме посилання на Як це працює" translate="no">​</a></h2>
<p>Це автомат Мура. Кожен стан має чотири підпрограми, і автомат викликає їх в однаковому порядку при кожному запуску:</p>
<ol>
<li class=""><strong>Entry</strong>: викликається один раз, коли стан входить в дію.</li>
<li class=""><strong>Execute</strong>: викликається при кожному запуску, тут виконується робота стану.</li>
<li class=""><strong>CheckLeave</strong>: викликається при кожному запуску після Execute. Це <strong>єдине</strong> місце, де запитується новий стан.</li>
<li class=""><strong>Leave</strong>: викликається один раз, коли стан залишається.</li>
</ol>
<p>Увесь рушій — це одна функція. Це версія на C:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">Module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token function" style="color:rgb(130, 170, 255)">_HandleStateTransition</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">void</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* A corrupted actual state must not index outside of the table: start again from the default state. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">MODULE</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_STATE_COUNT </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;=</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_SM_ActualState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_SM_ActualState </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">MODULE</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_STATE_1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_SM_NewState    </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">MODULE</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_STATE_1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">Module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token function" style="color:rgb(130, 170, 255)">_CallRoutine</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_SM_StateRoutines</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_SM_ActualState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">entry</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">else</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* Actual state is in the valid range */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* Execute function shall be used for main execution of actual state */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">Module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token function" style="color:rgb(130, 170, 255)">_CallRoutine</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_SM_StateRoutines</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_SM_ActualState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">execute</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* CheckLeave function shall be used for check leave condition of actual state */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">Module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token function" style="color:rgb(130, 170, 255)">_CallRoutine</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_SM_StateRoutines</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_SM_ActualState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">checkLeave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* In case of an invalid request (made in checkLeave), switch to the default/error state. */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">MODULE</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_STATE_COUNT </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;=</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_SM_NewState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_SM_NewState </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">MODULE</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_STATE_1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">else</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* Requested state is in the valid range */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* Leave and entry functions shall be executed only in case if the state has to be changed to another state */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_SM_ActualState </span><span class="token operator" style="color:rgb(137, 221, 255)">!=</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_SM_NewState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">Module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token function" style="color:rgb(130, 170, 255)">_CallRoutine</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_SM_StateRoutines</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_SM_ActualState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">leave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_SM_ActualState </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_SM_NewState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">Module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token function" style="color:rgb(130, 170, 255)">_CallRoutine</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_SM_StateRoutines</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">module</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain">_SM_ActualState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">entry</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">else</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* No new state required */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></div></code></pre></div></div>
<p>Оскільки перехід запитується лише в <code>CheckLeave</code>, вхід у стан і вихід із нього виконуються завжди як пара, у відомому порядку, а виходи залежать лише від стану. Непотрібна підпрограма може бути <code>NULL</code> (<code>nullptr</code> у C++), і її буде пропущено.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-was-fixed">Що було виправлено<a href="https://embedbits.cz/uk/blog/finite-state-machines#what-was-fixed" class="hash-link" aria-label="Пряме посилання на Що було виправлено" title="Пряме посилання на Що було виправлено" translate="no">​</a></h2>
<p>Перша версія шаблону, яку я тут опублікував, мала кілька помилок. Я пройшов по ній ще раз, зібрав її та запустив із тестом, що записує порядок викликів. Ось зміни:</p>
<ul>
<li class="">Початкове значення змінних стану було залишком назви з іншого проєкту (<code>APPCORE_HANDLER_STATE_1</code> замість <code>&lt;MODULE&gt;_STATE_1</code>).</li>
<li class="">В останньому рядку таблиці стояло <code>State_2_Leave</code> замість <code>State_3_Leave</code>. Код компілювався й викликав неправильну підпрограму при виході з третього стану. Тепер таблиця індексується станом (<code>[&lt;MODULE&gt;_STATE_3] = { ... }</code>), тож позицію рядка переплутати неможливо.</li>
<li class=""><code>Init</code> не запускав підпрограму entry першого стану.</li>
<li class="">Номери станів не перевірялися. Пошкоджений поточний стан або запит неіснуючого стану індексували за межами таблиці. Тепер перевіряються обидва, і автомат повертається до першого стану, який є безпечним. Запит перевіряється <strong>після</strong> <code>CheckLeave</code>, бо саме там він робиться. Моє перше виправлення перевіряло його до цього, і тест це виявив, що є гарним доказом того, чому такий тест вартий зусиль.</li>
<li class="">Підпрограма з вказівником <code>NULL</code> пропускається замість аварії.</li>
<li class="">Відсутнє попереднє оголошення (forward declaration) і коментар у заголовку, що після заміни назв перетворився на нісенітницю.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="версія-на-c">Версія на C++<a href="https://embedbits.cz/uk/blog/finite-state-machines#%D0%B2%D0%B5%D1%80%D1%81%D1%96%D1%8F-%D0%BD%D0%B0-c" class="hash-link" aria-label="Пряме посилання на Версія на C++" title="Пряме посилання на Версія на C++" translate="no">​</a></h2>
<p>У C++ той самий шаблон — це клас. Стан — <code>enum class</code>, підпрограми — приватні функції-члени, а таблиця — <code>constexpr</code>:</p>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">Button</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">enum</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">State</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token base-clause">std</span><span class="token base-clause double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token base-clause keyword" style="font-style:italic">uint8_t</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> Idle </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0u</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> Pressed</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> Count </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Init</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">noexcept</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">Task</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">noexcept</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    State </span><span class="token function" style="color:rgb(130, 170, 255)">GetState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">noexcept</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> stateMachine</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">GetState</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">private</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">using</span><span class="token plain"> Machine </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> fsm</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">StateMachine</span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token plain">Button</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> State</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token generic-function function" style="color:rgb(130, 170, 255)">static_cast</span><span class="token generic-function generic class-name operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token generic-function generic class-name" style="color:rgb(255, 203, 107)">std</span><span class="token generic-function generic class-name double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token generic-function generic class-name" style="color:rgb(255, 203, 107)">size_t</span><span class="token generic-function generic class-name operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">State</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">Count</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">IdleEntry</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">   </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">IdleExecute</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">   </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">IdleCheckLeave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain">   </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">IdleLeave</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">PressedEntry</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">/* ... */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">constexpr</span><span class="token plain"> Machine</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">Table </span><span class="token function" style="color:rgb(130, 170, 255)">MakeTable</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">noexcept</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token plain"> Machine</span><span class="token double-colon punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">Table table</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">    Machine stateMachine</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></div></code></pre></div></div>
<p>Запит — це <code>stateMachine.RequestState(State::Pressed)</code> у підпрограмі <code>CheckLeave</code>. Рядок таблиці має належати своєму стану, і це перевіряється <strong>під час компіляції</strong> за допомогою <code>static_assert</code>, тож помилка з першої версії просто не збереться. Немає heap, винятків і RTTI, тож це відповідає звичним embedded- та MISRA-подібним обмеженням. Версію на C++ скомпільовано з <code>-Wall -Wextra -Wconversion -pedantic -fno-exceptions -fno-rtti</code> і запущено під санітайзерами address та undefined behavior з тими самими сценаріями, що й версію на C: порядок викликів, запит, недійсний запит, пошкоджений стан і пропущені підпрограми <code>NULL</code>.</p>
<p>Друга частина цієї серії, <a class="" href="https://embedbits.cz/uk/blog/fsm-in-practice-button">кнопка з debounce та довгим натисканням</a>, показує шаблон у реальному модулі.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ліцензія">Ліцензія<a href="https://embedbits.cz/uk/blog/finite-state-machines#%D0%BB%D1%96%D1%86%D0%B5%D0%BD%D0%B7%D1%96%D1%8F" class="hash-link" aria-label="Пряме посилання на Ліцензія" title="Пряме посилання на Ліцензія" translate="no">​</a></h2>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">Copyright (c) 2024 Marek Petrinec, Jan Sima</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">Permission is hereby granted, free of charge, to any person obtaining a copy</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">of this software and associated documentation files (the "Software"), to deal</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">in the Software without restriction, including without limitation the rights</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">to use, copy, modify, merge, publish, distribute, sublicense, and/or sell</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">copies of the Software, and to permit persons to whom the Software is</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">furnished to do so, subject to the following conditions:</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">The above copyright notice and this permission notice shall be included in all</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">copies or substantial portions of the Software.</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">SOFTWARE.</span><br></div></code></pre></div></div>]]></content:encoded>
            <category>Вбудовані системи</category>
        </item>
    </channel>
</rss>