IAR AVR. Си + Ассемблер

Итак, скачиваем проект generator_IAR.rar, распаковываем его и открываем в IARe.
Создаем новый файл File > New > File.
Называем его encoder.asm и сохраняем в папке проекта.
Подключаем этот файл к проекту Project > Add Files..
Сишный файл encoder.c удаляем из проекта Project >Remove
Сохраняем проект.

Для работы с энкодером у нас три функции:

void ENC_InitEncoder(void)
void ENC_PollEncoder(void)
unsigned char
ENC_GetStateEncoder(void)

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

void ENC_SetStateEncoder(unsigned char var)

Сейчас мы напишем ассемблерные аналоги этих функций. Сразу вас предупрежу, синтаксис IAR`овского ассемблера несколько отличается от AVR`овского. Взять хотя бы такую простую вещь — в IAR`e ассемблерные команды должны писаться с отступом. Если этого не сделать, компилятор выдаст ошибку. 


Структура ассемблерного модуля

   Ассемблерные подпрограммы должны находиться внутри модуля. Модуль имеет следующую структуру:

MODULE имя_модуля
   //подключение заголовочных файлов

   RSEG DATA //сегмент данных
   //здесь можно объявить переменные

   RSEG CODE //сегмент кода
   //здесь будут ассемблерные подпрограммы

ENDMOD
END


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

Назовем наш модуль encoder и напишем его каркас.

MODULE encoder
  #include
  #include "encoder.h"

  RSEG NEAR_Z

  RSEG CODE
   
//инициализация порта
ENC_InitEncoder:
      ret

//опрос энкодера
ENC_PollEncoder:
     ret

//возвращает значение буфера  
ENC_GetStateEncoder:
    ret

//загружает значение в буфер
ENC_SetStateEncoder:
    ret

ENDMOD
END


ioavr.h мы подключили, потому что в программе используются имена регистров AVRа.
encoder.h – это заголовочный файл от сишного исходника. Его нужно немного изменить - удалить подключение ioavr.h и прототипы всех функций.

NEAR_Z — это название области памяти данных (ОЗУ) используемой для статических и глобальных переменных инициализируемых нулем.

Дальше вроде все понятно - 4 пустых подпрограммы.

В исходнике энкодера используются 2 глобальных переменных. В ассемблерном файле они нам тоже нужны. Объявим их:

  RSEG NEAR_Z

stateEnc  DS
bufEnc    DS 1   


stateEnc – название переменной, DS – директива для резервирования оперативной памяти под байтовую переменную, 1 — количество резервируемых байтов.
Заметьте, никаких адресов ОЗУ мы не указываем - за нас это сделает компилятор.

Для того чтобы наши подпрограммы были доступны в других модулях нужно добавить еще одно объявление:

MODULE encoder
  #include
  #include "encoder.h"

  PUBLIC ENC_InitEncoder
  PUBLIC ENC_PollEncoder
  PUBLIC ENC_GetStateEncoder
  PUBLIC ENC_SetStateEncoder
...

Теперь нужно наполнить ассемблерные подпрограммы/функции содержимым.

Функция инициализации

ENC_InitEncoder:
  cbi DDR_Enc, Pin1_Enc
  cbi DDR_Enc, Pin2_Enc
  sbi PORT_Enc, Pin1_Enc
  sbi PORT_Enc, Pin2_Enc
  ret

DDR_Enc,  PORT_Enc … это макроопределения из файла encoder.h

Функция записи в буфер

   IAR может передавать аргументы в функцию двумя способами - старым, который использовался в IAR`e версии 1.х и новым, который используется в старших версиях. При написании смешанных проектов  рекомендуется использовать старый способ.  Для этого перед  функциями пишется ключевое слово __version_1. Ниже об этом еще пойдет речь.
   Перед вызовом функции, значения аргументов копируются в регистры R16 - R23. В описании на компилер приводится такая табличка:
регистры используемые компилятором IAR для передачи аргументов в функцию
Где b – 8-ми разрядные переменные, w – 16-ти разрядные, l – 32 разрядные.
 
Она дает нам информацию, какие регистры используются для различных типов данных. 
Если выделенных регистров для передачи аргументов не хватает, компилер использует стек, но я с этой темой пока не разбирался
 
Нашей функции записи при вызове передается байтовая переменная, значит компилятор (согласно таблице) поместит ее значение в R16. Нам остается только забрать его оттуда и записать в буфер.

ENC_SetStateEncoder:
  sts bufEnc, r16
  ret

Функция чтения буфера

Для возврата значений компилятор IAR использует регистры R16 – R19. В табличке ниже расписаны варианты для разных типов данных.
регистры используемые комиплятором IAR для возврата значений
Функция чтения должна считать значение буфера, поместить его в R16 (согласно таблице), очистить буфер и выйти.

ENC_GetStateEncoder:
  lds r16, bufEnc
  clr r17
  sts bufEnc, r17
  ret

Функция опроса энкодера

   Эта функция ничего не возвращает и не принимает, но зато делает самую главную работу — опрашивает энкодер. Для выполнения различных операций "внутри" функции IAR использует регистры  R0–R3, R16–R23 и R30–R31 (так называемые scratch registers).

ENC_PollEncoder:
  clr r16                           //очищаем R16 потому что он нам понадобится ниже
  sbic PIN_Enc, Pin1_Enc   //записываем состояние выводов энкодера в r16
  sbr r16, 1                     
  sbic PIN_Enc, Pin2_Enc

  sbr r16, (1<<1)            
  lds r17, stateEnc            //считываем переменную, в которой хранятся старые  
  andi r17, b'00000011     //состояния энкодера и выделяем последнее
  cp r16, r17                    //сравниваем с текущим  
  breq Exit                       //если равны, значит ничего не изм. — выходим
  lds r17, stateEnc            //если не равны, считываем   stateEnc,
  lsl r17                           //сдвигам влево на 2 разряда
  lsl r17                           //чтобы добавить текущее состояние
  or r16, r17                    //добавляем с помощью операции ИЛИ
  sts stateEnc, r16            //сохраняем в ОЗУ
  cpi r16, b'11100001       //сравниваем получившуюся последовательность
  breq LeftSpin                 //с константами, переходим если равно
  cpi r16, b'11010010
  breq RightSpin
  ret

LeftSpin:
  ldi r16, LEFT_SPIN     //если вращение влево, записываем в r16 константу    
  rjmp LoadToRam        // LEFT_SPIN и прыгаем на метку

RightSpin:
  ldi r16, RIGHT_SPIN   //если вправо, записываем в r16 RIGHT_SPIN
 
LoadToRam:
  sts bufEnc, r16          //сохраняем r16 в буфер
 
Exit:
  ret

Все. Модуль написан - IAR AVR. Ассемблерный модуль.
Последний штрих - нужно сообщить компилятору, что используемые нами функции находятся в другом модуле и указать, что вызов функций должен осуществляться старым методом. Для этого используются ключевые слова extern  и __version_1 соответственно. Добавляем перед main`ом.

extern void ENC_InitEncoder(void);
extern void ENC_PollEncoder(void);
extern unsigned char ENC_GetStateEncoder(void);
__version_1 extern void ENC_SetStateEncoder(unsigned char val);

Всего одна функция у нас принимает аргументы, поэтому  __version_1 я добавил только перед ней.
Запускаем компиляцию и сборку - F7....  Грузим прошивку в микроконтроллер и проверяем ее работоспособность... Все работает. Продолжение следует...

Файлы

IAR AVR. Си+Ассемблер

У вас недостаточно прав для комментирования.