Ремонт принтеров, сканнеров, факсов и остальной офисной техники


назад Оглавление вперед




[5]

5 Особенности реализации

5.1 Поддержка прерываний

Компилятор PICC полностью поддерживает прерывания контроллера. Таким образом вам не придется писать ни строчки ассемблерного кода при написании полноценных обработчиков прерываний. Для описания функции, которая и будет являться обработчиком, необходимо воспользоваться квалификатором interrupt. Эта функция будет напрямую вызвана в случае возникновения прерывания. Но компилятор оформит эту функцию специальным образом. Прежде всего будут сохранены и восстановлены все регистры, которые были задействованы в обработчике и, кроме того, для выхода из функции бкдет использоваться оператор retfie.

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

Опуская поддержку прерываний в контроллерах базового класс, сразу переходим к контроллерам среднего класса. Вот пример простейшего обработчика прерываний: long tick count; void interrupt tc int(void) {

++tick count;

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

Следует отдельно остановиться на сохранении контекста при выполнении обработчика прерывания.

Все знают, что при возникновении прерывания контроллер автоматически сохраняет только регистр PC, чего явно недостаточно для работы нормальной программы. Поэтому программист должен сам позаботиться о сохранении всех важных регистров и объектов. Поскольку система прерываний контроллеров среднего класс не поддерживает приоритеты, то особых проблем с сохранением контекста не возникает. Более того, компилятор PICC сам отслеживает какие переменные и объекты были использованы в обработчике прерываний и сохраняет их. Это очень удобно при написании тривиальных обработчиков прерываний. Когда требуется тонкое управление процедурой сохранения-восстановления контекста, то можно, к примеру, воспользоваться встроенным ассемблером, так как фрагменты ассемблерного кода, которые компилятор встречает в обработчике прерываний, не сканируются на предмет выявления переменных, нуждающихся в сохранении.

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

В заключении хочется рассказать, как собственно управлять прерываниями (разрешать и запрещать). С PICC это делается очень просто:

ADIE = 1; Разрешитьпрерывание от АЦП

PEIE = 1; Разрешитьвсе прерывания от периферийных устройств

ei(); Разрешитьвсе прерывания

di(); Запретитьвсе прерывания


5.2Совмещение кода C и Assembler

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

В первом случае, определенная функция может быть написана полностью на ассемблере как внешний файл с расширением .as, скомпилирована с помощью ASPIC и включена в двоичный образ с помощью компоновщика. Эта технология позволяет передавать аргументы и принимать результат между С и ассемблерной программами. Для этого, прежде всего, необходимо включить адекватное описание внешней функции в С-программе. Допустим, нам необходима функция сдвига в лево через флаг переноса, и мы решили реализовать ее на ассемблере:

extern char rotateleft(char);

В примере, приведенном выше, мы описали внешнюю функцию под названием rotate left ( ), которая получает в качестве аргумента символ (char) и, соответственно, возвращает символ, только сдвинутый влево в соответствии с условим задачи. Текст этой архи-сложной функции поставляется в виде отдельного файла (.as), который, к тому же, и компилируется отдельно с помощью ASPIC. Вот вам возможная реализация этой функции: processor 16C84

psect text0,class=CODE,local,delta=2 globalrotate left

signatrotate left,4201

rotate left ; Аргумент передается через регистр W movwf?a rotate left

; Результат сдвига помещаем опять в W rlf?a rotate left,w

; Результат должен находиться в W return

FNSIZE rotate left,1,0

global?a rotate left

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

ВНИМАНИЕ! Чтобы все работало правильно ассемблерная функция должна брать аргументы из правильного места и правильно оформлять результат. Механизм выделения памяти под локальные переменные (через fnsize), аргументы и результат детально обсуждается в руководстве и его таки надо хорошо понимать, прежде чем лезть со своим ассемблером в С-программу!!!

5.3Передача аргументов в функцию

Метод передачи функции аргументов определяется их размерами и количеством.

Если передается только один аргумент типа char, то используется регистр W.

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


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

В случае передачи списка аргументов, создает временный список аргументов и передает указатель на него.

Для примера рассмотрим простой вызов функции: void test(int a, int b, int c)

Очевидно, что эта функция получит все аргументы в свою область аргументов. В случае вызова test( 0x65af, 0x7288, 0x080c) будет получен следующий код:

movlw 0AFh

movwf (((? test))&7fh) movlw 065h

movwf (((? test+1))&7fh)

movlw 088h

movwf ((0+((? test)+02h))&7fh)

movlw 072h

movwf ((1+((? test)+02h))&7fh)

movlw 0Ch

movwf ((0+((? test)+04h))&7fh) movlw 08h

movwf ((1+((? test)+04h))&7fh) lcall ( test)

Начало области аргументов определяется меткой состоящей из знака вопроса, знака подчеркивания и названия функции (? test). Эта величина принимается за базу, к которой прибавляется смещение для доступа ко всем аргументам. Таким образом первый аргумент будет доступен по адресам ? test и ? test+1.

Бывает полезно создавать пустые функции с заданными аргументами, компилировать программу с включенным флагом -S для контроля генерируемого кода.

5.4 Возвращение результата из функции

Передача результата из функции осуществляется следующим образом: Значения размером 8-бит

В микроконтроллерах среднего класса, байтовые значения возвращаются через рабочий регистр W. Пример:

char return 8(void)

return 0;

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

movlw 0 return

Значения размером 16-бит

Значения размером 16 и 32 бита возвращаются через память. Пример:

int return 16(void)

return 0x1234;

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



[стр.Начало] [стр.1] [стр.2] [стр.3] [стр.4] [стр.5] [стр.6] [стр.7]