Итак, скачиваем проект 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а.
NEAR_Z — это название области памяти данных (ОЗУ) используемой для статических и глобальных переменных инициализируемых нулем.
Дальше вроде все понятно - 4 пустых подпрограммы.
RSEG NEAR_Z
stateEnc DS 1
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
Функция записи в буфер
ENC_SetStateEncoder:
sts bufEnc, r16
ret
Функция чтения буфера
Для возврата значений компилятор IAR использует регистры R16 – R19. В табличке ниже расписаны варианты для разных типов данных. 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
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
Последний штрих - нужно сообщить компилятору, что используемые нами функции находятся в другом модуле и указать, что вызов функций должен осуществляться старым методом. Для этого используются ключевые слова 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);
Запускаем компиляцию и сборку - F7.... Грузим прошивку в микроконтроллер и проверяем ее работоспособность... Все работает. Продолжение следует...
Comments
еxtеrн vоid мy_iнt_fуnс (вoiд), в ASM, прописал как: РUВLIС мy_iнt_fуnс
…
рragma вectоr=номер
iнtеrruрt воid мy_iнt_fуnc (воiд), здесь комп. показал ошибку
ORG TIMER1_COMPB_ve ct
rjmp MY_test_func
….
Спасибо челу под ником BSVi (сайт bsvi_pp_ua) , подсказал в конце концов
:-)
Code:
NAME TIM0_VECTOR
#include <iom16.h>
COMMON INTVEC
ORG TIMER0_COMP_vect
rjmp Timer0_comp
RSEG CODE
Timer0_comp:
st -Y,R16 ; Push used registers on stack
in R16,SREG ; Read status register
st -Y,R16 ; Push Status register
in R16,PORTB ; Load in value from port B
com R16 ; Invert it
out PORTB,R16 ; Output inverted value to port B
ld R16,Y+ ; Pop status register
out SREG,R16 ; Store status register
ld R16,Y+ ; Pop Register R16
reti
ENDMOD
END
Тут смысл такой.
Определяешь в сегменте таблицы прерываний COMMON INTVEC ассемблерный переход на обработчик прерывания.
Сам обработчик описываешь в сегменте RCEG CODE.
Данный код нужно сохранить в ассемблерном файле и подключить его в IAR`овскому проекту.
Что вроде этого:
Code:
unsigned int timer = 0;
unsigned char state_timer = 0;
void TIM_SetTimer(unsigned int data)
{
unsigned int tmp = SREG;
__disable_interrupt();
state_timer = TIMER_WORK;
timer = data;
SREG = tmp;
}
unsigned char TIM_GetStateTimer(void)
{
if (state_timer == TIMER_EXPIRED) return 1;
return 0;
}
#pragma vector = TIMER0_OVF_vect
__interrupt void Timer0Ovf(void)
{
TCNT0 = 0xe6;
if (state_timer == TIMER_WORK){
if (timer != 0) {
timer--;
}
else{
state_timer = TIMER_EXPIRED;
}
}
BUT_Debrief();
IND_Update();
}
Только переписать это на ассемблере.
В этом примере доступ к переменной timer осуществляется через функции TIM_SetTimer() и TimGetStateTime r().
RSS feed for comments to this post