ТРИТОН-электронные компоненты

Поставка электронных компонентов - 718-84-05
Тритон-электронные компоненты

 

www.trt.ru

 


Приемы эффективного проектирования

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

В общем случае проект можно разделить на три уровня по степени специфичности кода и принципам использования:

Название

Описание

Представление

Степень адаптации

Алгоритмический

уникален, инкапсулирует основную функциональность проекта

исх. тексты

максимальная

Вспомогательный

набор типовых решений, адаптированных под текущую задачу

исх. тексты

умеренная

Системный

набор типовых решений, не требующих адаптации под проект

библиотеки / исх. тексты

нет

Алгоритмический уровень может разбиваться на подуровни и/или отдельные модули, но смысл приведенной классификации не изменится. Например, часто используемые из алгоритмического модуля подпрограммы целесообразно вынести в отдельный файл, однако подключать его имеет смысл через #include , а не в виде модуля, и уровень файла остается прежним – алгоритмический. Системный уровень используется, в первую очередь, для драйверов периферии. Правильно спроектированный системный уровень допускает замену модуля на альтернативный без потери функциональности (например, драйвер аппаратного устройства на программный эмулятор).

Методом исключения оставшиеся модуля следует отнести к вспомогательному уровню. Как правило, это алгоритмические библиотеки, допускающие адаптацию под текущую задачу (способы передачи и типы параметров, использование памяти и периферии) и вызовы из смежных модулей. Типичные подпрограммы – задержки, преобразование типов, операции с памятью, полная и/или частичная инициализация.

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

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

Стек данных

Проблема хранения временных переменных эффективно решается с применением стека данных. Поскольку архитектура 8-разрядных микроконтроллеров PIC не подразумевает существования такого стека, реализовать его можно при помощи косвенной адресации. Наиболее подходит для этого семейство PIC 18, поскольку содержит три регистра косвенной адресации с автоинкременом-декрементом.

Описание :
STACKLEN  =      0x20
Stack     res    STACKLEN

Инициализация :
lfsr      x , Stack -1

Запись :
push   WREG      movwf     PREINCx
push   GPR       movff     GPR, PREINCx

Чтение :
pop   WREG       movf         POSTDECx, w
pop   GPR        movff        POSTDECx, GPR


; x  = номер используемого FSR

Применение пары PREINC-POSTDEC делает возможным обращение к вершине стека без извлечения значения. Пример – построение цикла:

     movlw      .10
     movwf      PREINC 2        ; подготовка счетчика
Loop :
     …                          ; тело цикла
     decfsz     INDF 2, f       ; счет без извлечения из стека
     bra        Loop
     movf       POSTDEC 2, f    ; освобождение стека

Очевидно, что все операции записи и чтения стека должны быть парными. Проверить правильность работы подпрограмм на этапе отладки можно довольно просто: установить точки останова на обращения к ячейкам Stack -1 и Stack + STACKLEN . Помимо этого хорошо принять практику написания кода таким образом, чтобы минимизировать риск рассогласования пар записи-чтения. В вышеприведенном примере операция освобождения стека является явным потенциальным источником ошибок, и описать цикл более надежно можно так:

     movlw    .10              ; подготовка счетчика
LOOP :
     movwf    PREINC2
     …                         ; тело цикла

     decfsz   INDF2, w         ; счет без извлечения из стека
     bra      LOOP

Здесь операция освобождения стека отсутствует, цикл выглядит привычно и прозрачно. Однако есть и минусы – тело цикла становится длиннее на 1 инструкцию, и при декременте счетчика задействуется WREG . Для всех случаев, где возможно, рекомендуется использование именно второй конструкции.

Предлагаемая реализация стека не накладывает ограничений на применение из прерываний, в т.ч. для сохранения контекста.

Синхронные системы

Привязка задачи к реальному времени и синхронизация процессов между собой требуются практически в каждом проекте. Очевидно, что программные задержки не являются оптимальным решением, т.к. зависят от тактовой частоты, синхронизируются относительно текущего момента вместо абсолютных временных меток, и не позволяют выполнять фоновые задачи. Этих недостатков лишен программно-аппаратный метод синхронизации, в котором таймер используется в качестве независимого опорного генератора высокой точности. Например, используя Timer 0 для генерации прерывания с периодом 1 мс и каскад счетчиков-делителей в прерывании, можно получить набор временных меток с интервалами 1-10-100-1000 мс, которые в дальнейшем могут использоваться подпрограммами генерации задержек и процессами при взаимной синхронизации. Поскольку при таком подходе требуется только ожидать установки нужного флага, то подпрограммы задержек могут вызывать фоновые подпрограммы, имеющие недетерминированное время завершения:

Delay10m                         ; задержка WREG * 10 мс
     rcall      Idle         ; низкоприоритетные фоновые задачи
     btfss        F10m           ; проверка 10-мс флага
     bra          Delay10m
     bcf          F10m
     addlw        -1             ; декремент счетчика в WREG
     bz           Delay10m       ; повтор, если не 0
     return

Строго говоря, максимальное время выполнения не должно превышать период опорных меток, иначе может возникать пропуск тактов – из этого следует, что с фоновыми процессами предпочтительнее использовать задержки с максимально грубыми метками: 5 х 10 мс лучше, чем 50 х 1 мс. С другой стороны, максимальная погрешность задержки с несинхронным началом равна периоду меток, т.о. использование высокочастотной опоры повышает точность.

Генераторы разнообразных тайм-аутов (например, отключение подсветки при бездействии, ожидание ответа от медленного устройства и другие, измеряющиеся сотнями мс и более) целесообразно строить на основе выделенного инкрементального счетчика, работающего в прерывании, и устанавливающего флаг при достижении заданного значения. Основному процессу необходимо периодически обнулять значение таймера (на это требуется всего одна инструкция) и проверять состояние флага переполнения. Если действие при тайм-ауте не требует использования существенных ресурсов и вызова подпрограмм алгоритмического уровня (см. «Логическая организация проекта»), то выполняться оно может непосредственно в прерывании (например, отключение подсветки).

Максимальная автоматизация процессов через прерывания – ключ к надежности и быстродействию системы.

# define

Принцип действия директивы предельно прост:
     # define [ ident ] [ string ]
при компиляции заменяет все совпадающие идентификаторы [ ident ] на строку [ string ]. Выгоды очевидны:
- самодокументируемость: название идентификатора говорит о его назначении;
- единство: модификация определения = модификация всех обращений;
- безошибочность обращений: адресуемые биты привязаны к своим регистрам.

Хорошей практикой может стать доработка процессорных inc-файлов с применением директивы #define для всех регистров специальных функций. Готовые к использованию примеры таких файлов можно найти в разделе, посвященном данной статье.

 

Daname.DesignLab
(495) 668-26-46                 © Тритон-электронные компоненты 2005                triton@trt.ru