Есть ли в мире что-нибудь более постоянное, чем временные переменные? Просматривая тематический форум, увидел традиционное «Скетч не работает, подскажите в чем дело» — таких постов немного меньше, чем у всех, но в заголовке упоминалась работа с SD-картой, поэтому я решил взглянем.
Больше всего меня порадовала фраза о том, что «скорее всего дело в карте, но я решил попросить посмотреть код, может, вы увидите что-то интересное» - за точность цитаты не ручаюсь, но я правильно передал смысл.
Действительно, там наблюдалось много интересного; мой взгляд поймал выражение
Более того, эта переменная затем использовалась как константа при запуске задержки.unsigned long Interval = 2000; .
while (micros() < StartInterval + Interval) {};
Давайте оставим из уравнения способ обращения со временем; это не то, о чем мы сейчас говорим.
Сначала, действуя автоматически, я написал, что нужно использовать #define, «но я думал об этом, но у меня во рту был сыр».
Может быть, я чего-то не знаю, и использование констант в этом формате может быть полезно при определенных условиях? Вдумчивое чтение мануалов к различным микроконтроллерам (MCU) привело к интересным выводам, которыми (вместе с ответом на мой вопрос) я поделюсь под катом.
Для начала зафиксируем ряд положений, касающихся архитектуры современных микроконтроллеров, на которые будем опираться в дальнейших рассуждениях.
Во-первых, в микроконтроллере имеется как минимум два типа памяти — память программ и память данных, и доступ к ним осуществляется по-разному.
Как правило, мы имеем две физически разделенные шины, даже если адресное пространство комбинированное и линейное, у нас часто имеются разные команды доступа к разным типам памяти и адресное пространство может перекрываться.
Во-вторых, эти два типа памяти могут иметь разную ширину слова (этот термин я использую для обозначения количества бит в слове) и она не обязательно должна быть равна ширине регистров МК (что, в свою очередь, может изменение путем объединения регистров в группы), что придает особую пикантность дальнейшему обсуждению темы.
Проиллюстрируем вышесказанное конкретными примерами архитектур, на которые мы в дальнейшем будем опираться.
Номер ноль — это, скорее всего, чемпион по архитектурной красоте, но не по эффективности, к сожалению (моя первая любовь в технике), PDP 11 от DEC. К сожалению, ее уже нет с нами, но память о ней навсегда останется в наших сердцах (не только память, многие современные архитектуры явно были вдохновлены ее идеями, например MSP430 от TI).
Ширина регистра (Р) — 16 бит, программные слова (Р) — 16 бит, слова данных (D) — 16 бит (старший и младший байт слова доступны отдельно), адресные шины (А) — 16 бит ( 20 в расширенном режиме), адресное пространство линейное и комбинированное, все команды однородны.
Первая архитектура — 8051 от Intel, несомненная классика (и до сих пор не умершая), так что это отличный кандидат. Ширина регистра - 8 бит, программные слова - 8 бит, слова данных - 8 бит, адресные шины - 16 бит, адресное пространство перекрывается и имеет 3 секции (программа, данные, расширенные данные), имеется три разные команды чтения из разных секций.
, регистры могут образовывать пары регистров.
Вторая архитектура — AVR (Tiny, Classic, Mega) от Atmel — широко распространена (Arduino), достаточно хороша, поэтому не вижу причин не рассматривать ее, тем более, что программа написана под нее.
Ширина регистра 8 бит, программных слов 16, слов данных 8, адресных шин 16, адресное пространство перекрывается и имеет две секции (программы и данные), имеются две разные команды чтения из разных секций, могут образовываться регистры.
зарегистрировать пары.
Третья архитектура — несомненный лидер на данный момент, Cortex-M от ARM, поэтому не рассматривать ее я не вижу возможности.
Я не буду указывать конкретную реализацию, их тысячи.
Ширина регистра 32 бита, программные слова 32 бита (16 в режиме Thumb), слова данных 32 бита, адресные шины 32 бита, адресное пространство линейное и комбинированное, все команды однородны.
Несомненно, есть еще много архитектур, достойных рассмотрения, но некоторые из них представляют лишь исторический интерес (i8048, PDP8), другие не так распространены (Sparc, MIPS, PDP11/78), третьи мне известны весьма поверхностно (PIC, Scenix ), а другие мне откровенно не нравятся (HC08, x86).
Впрочем, будут упомянуты и интересные решения из этих МК, но основное внимание будет уделено четырем ранее перечисленным.
Итак, давайте точно сформулируем задачу — нам нужно поместить в регистр процессора какое-то заранее определенное число, причем его значение статически определяется во время компиляции (константа).
Как это можно сделать и в чем преимущества и недостатки каждого из возможных способов – рассмотрим подробнее.
Первый и очевидный способ — прямая загрузка — константа является частью команды непосредственно, то есть само командное слово состоит из кода операции и самой константы в чистом виде.
Этот метод лишен каких-либо недостатков, так или иначе свойственных другим методам, но.
он должен быть практически применим, а это значит, что ширина программного слова должна быть больше ширины регистра (P> P), иначе мы просто не сможем реализовать такие команды, как можем.
И не просто больше, а значительно больше, чтобы можно было реализовать более одной команды загрузки (или хотя бы загрузить более чем в один регистр).
Этот метод прекрасно используется МК типа 2, но для остальных рассматриваемых он просто невозможен, так как не выполняется их главное условие.
Изменить соотношение ширины регистра к программному слову и обеспечить выполнение условия P
P. Назовем этот метод прямой загрузкой, так как аналогичный метод адресации вызывался в архитектуре 0. То есть команда остается стандартной длины и ее тело не содержит никакой информации о самой константе, но следующее за этой командой программное слово рассматривается как сама константа.
Заметим, что для применимости этого метода необходимо условие P> =P и это накладывает ограничения на архитектуру микроконтроллера, но существенно менее слабые, чем при прямой загрузке.
Кроме того, никто не мешает нам расширять этот подход и при необходимости использовать для представления константы более одного программного слова.
Поскольку этот метод используется чуть реже, чем всегда, рассмотрим его подробнее.
Конкретная реализация возможна либо в виде обычной инструкции пересылки со специальным методом адресации (027 в моей любимой архитектуре), либо в виде специальных инструкций загрузки (LDI).
Преимущество очевидно — мы можем сформировать абсолютно любую константу произвольной длины, а реализовать этот подход аппаратно очень просто.
Недостатки не столь очевидны, но они есть — увеличивается время выполнения команд (по крайней мере, на время выборки второго и последующих слов программы), нарушается регулярность выполнения команд (что затрудняет деассемблирование), и возникает неоднородность загрузки исполнительного конвейера (если он есть).
И последний (не уверен, что последний, но больше ничего придумать не могу), но не последний недостаток - это избыточность памяти, то есть каждый раз, когда в коде появляется константа, место в программе для него выделена память.
Он также зарезервирован для ранее описанных методов, но там мы все равно не смогли использовать часть команды, используемую для хранения константы, для других целей (честно говоря, утверждение спорное для VLIW-архитектур, но мы такое не рассматриваем) , так что все было не так уж и плохо, но тут приходится каждый раз выделять дополнительное слово памяти и этот недостаток начинает бросаться в глаза.
Но выхода нет, «за все в этом мире нужно платить».
На этом тему прямой и немедленной загрузки константы в регистр можно считать завершенной – мы рассмотрели все возможные способы ее реализации и видим, что наряду с несомненными преимуществами этот метод либо имеет ограниченное применение, либо выдвигает значительные требования к параметрам архитектуры микроконтроллера или вызывает увеличение объема программы.
Но для тех, кто знакомился с МК на примере архитектуры 0 (еще раз настоятельно рекомендую ознакомиться с ней, более прозрачной и чистой реализации, а также понятного описания я не встречал), Сразу возникнет вопрос, возможна ли косвенная адресация (код 037) в различных ее формах? Конечно, правильный ответ – «да», иначе я бы не задавал этот вопрос, но все не так просто.
Прежде всего немного о сути этого метода — команда содержит информацию не об операнде (в данном случае константе), а о его местоположении в адресном пространстве, откуда можно получить саму константу.
Конечно, этот метод при применении к константам может быть приемлем только в том случае, если эта адресная информация имеет меньшую ширину (и значительно меньше), чем ширина регистра.
То есть мы можем (в архитектуре 0) вместо реальной константы шириной 16 бит указать ее адрес такой же ширины, но в этом случае мы сохраняем все недостатки прямого метода и даже несколько усугубляем проблемы.
со временем выполнения команды, и при этом вообще ничего не делать, не имея взамен.
Если посмотреть на упомянутые мною архитектуры, то ни в одной из них не выполняется условие A
Войти , Пожалуйста.
О проектировании библиотеки, интерфейс которой вам интереснее прочитать 25,64% UART 20 20,51% SPI 16 62,82% USB 49 0% Другое - укажу в комментариях 0 17,95% Нет, случайно зашел в разрез (я думал там будут коты) 14 Проголосовали 78 пользователей.
28 пользователей воздержались.
Теги: #Программирование микроконтроллеров #Программирование микроконтроллеров
-
Военным Роботам Более 65 Лет!
19 Oct, 24 -
.Masterhost Снова Отключает Серверы
19 Oct, 24