Микроконтроллеры, АЦП, память и т.д Темы касающиеся микроконтроллеров разных производителей, памяти, АЦП/ЦАП, периферийных модулей... |
21.08.2010, 00:04
|
|
Почётный гражданин KAZUS.RU
Регистрация: 29.10.2006
Сообщений: 1,446
Сказал спасибо: 99
Сказали Спасибо 317 раз(а) в 233 сообщении(ях)
|
Как изменить адрес подпрограммы обработки прерывания?
Например, есть два режима работы контроллера. В одном режиме по прерыванию от таймера должны выполняться одни действия. В другом режиме - по этому же прерыванию - другие действия.
Можно, конечно, в едином обработчике (в начале) прерывания делать проверку и выполнять нужный участок кода, но это будет отнимать такты.
В идеале нужно написать две подпрограммы обработки прерывания в этих двух режимах.
И изменять адрес, по которому происходит переход на подпрограмму обработки прерывания в зависимости от режима.
Как это сделать на WinAVR ну и на CodeVision AVR.
Заранее спасибо.
Последний раз редактировалось Godzilla82; 21.08.2010 в 00:06.
|
|
|
|
21.08.2010, 00:34
|
|
Почётный гражданин KAZUS.RU
Регистрация: 13.12.2004
Сообщений: 3,172
Сказал спасибо: 11
Сказали Спасибо 692 раз(а) в 504 сообщении(ях)
|
Re: Как изменить адрес подпрограммы обработки прерывания?
Сообщение от Godzilla82
|
Можно, конечно, в едином обработчике (в начале) прерывания делать проверку и выполнять нужный участок кода, но это будет отнимать такты.
|
Так и делайте, это самое простое. Три-пять лишних тактов всего.
|
|
|
|
21.08.2010, 02:31
|
|
Почётный гражданин KAZUS.RU
Регистрация: 29.10.2006
Сообщений: 1,446
Сказал спасибо: 99
Сказали Спасибо 317 раз(а) в 233 сообщении(ях)
|
Re: Как изменить адрес подпрограммы обработки прерывания?
Сообщение от kison
|
Так и делайте, это самое простое. Три-пять лишних тактов всего.
|
У меня как раз такая задача, когда всё упирается в эти самые лишние 3-5 тактов. Нужно по адресу вектора прерывания поменять адрес перехода на нужную подпрограмму. Думаю, что это возможно сделать средствами СИ (пусть даже с ассемблерными вставками). Поэтому и спрашиваю.
|
|
|
|
21.08.2010, 02:53
|
|
Почётный гражданин KAZUS.RU
Регистрация: 13.12.2004
Сообщений: 3,172
Сказал спасибо: 11
Сказали Спасибо 692 раз(а) в 504 сообщении(ях)
|
Re: Как изменить адрес подпрограммы обработки прерывания?
Сообщение от Godzilla82
|
Думаю, что это возможно сделать средствами СИ (пусть даже с ассемблерными вставками). Поэтому и спрашиваю.
|
Возможно. Но не более 10000 раз, или сколько там флеш выдержит перезаписей.
И не в любом АВР, а только в тех, где есть команда SPM.
Можно попробовать еще переход по указателю, но оверхед будет больше. Заводите переменную 16 бит в регистрах. В нее грузите адрес нужного обработчика. В начале прерывания сохраняете в стеке R30 и R31, переносите в них адрес из своей регистровой переменной и icall. Выходит 2+2 такта на сохранение, 1 такт на movw, и 3 такта на icall. Это все нужно на ассемблере делать. Если и реальные обработчики на ассемблере, то можно переходить на них по ijmp, это 1 такт сэкономит. Но перед выходом надо будет извлечь из стека R30 и R31 обратно.
|
|
|
|
21.08.2010, 07:48
|
|
Почётный гражданин KAZUS.RU
Регистрация: 13.10.2007
Адрес: Беларусь
Сообщений: 8,048
Сказал спасибо: 60
Сказали Спасибо 3,954 раз(а) в 2,309 сообщении(ях)
|
Re: Как изменить адрес подпрограммы обработки прерывания?
Как вариант - можно переключать бутлодырь, но тогда нужно дублировать все прерывания
Вариант с флагом в начале прерывания самый простой и быстрый, если оптимизировать код - все будет прекрасно
|
|
|
|
21.08.2010, 11:08
|
|
Прохожий
Регистрация: 08.08.2010
Сообщений: 7
Сказал спасибо: 1
Сказали Спасибо 1 раз в 1 сообщении
|
Re: Как изменить адрес подпрограммы обработки прерывания?
Сообщение от Godzilla82
|
У меня как раз такая задача, когда всё упирается в эти самые лишние 3-5 тактов. Нужно по адресу вектора прерывания поменять адрес перехода на нужную подпрограмму. Думаю, что это возможно сделать средствами СИ (пусть даже с ассемблерными вставками). Поэтому и спрашиваю.
|
Думаю все можно решить при помощи Си, и мне кажется, что лучше в WinAvr.
Вопрос со сменой адреса вектора прерывания решается так:
PHP код:
|
...... int *my_int_ptr;//указатель
void my_int_1();//обьявление функции прерывания 1-й вариант void my_int_2();//обьявление функции прерывания 1-й вариант
int main() {
my_int_ptr = (int*)0;//присваиваем указателю значение 0
if(uslovie()) { my_int_ptr[2] = (int)my_int_1;//поменяли 2-й вектор } else { my_int_ptr[2] = (int)my_int_2;//поменяли 2-й вектор }
....................... return 0; }
void my_int_1() { INT_PROLOG ..... INT_EPILOG }
void my_int_2() { INT_PROLOG ..... INT_EPILOG }
|
А WinAvr удобнее(наверное) тем, что там можно порытся в исходниках и надыбать макросы, которые определяют пролог и эпилог прерывания, ну чтоб самому не писать ассемблерные вставки для сохранения и восстановления регистров. Возможно надо будет самому сделать вызов cli();
P.S. Хотя, если вам так дороги 5 тактов, то имеет смысл переписать прерывания на асме.
Последний раз редактировалось alex_312; 21.08.2010 в 11:12.
|
|
|
Сказали "Спасибо" alex_312
|
|
|
21.08.2010, 12:16
|
|
Почётный гражданин KAZUS.RU
Регистрация: 29.10.2006
Сообщений: 1,446
Сказал спасибо: 99
Сказали Спасибо 317 раз(а) в 233 сообщении(ях)
|
Re: Как изменить адрес подпрограммы обработки прерывания?
Сообщение от alex_312
|
Вопрос со сменой адреса вектора прерывания решается так...
|
PHP код:
|
#include ‹avr/io.h›
#include ‹avr/interrupt.h›
ISR1(SIG_OUTPUT_COMPARE0)
{
...
}
ISR2(SIG_OUTPUT_COMPARE0)
{
...
}
int main (void)
{
if(uslovie) ISR = ISR1;
else ISR = ISR2;
}
|
ISR = ISR1 - это, естесственно, неверно, просто как бы показывает, что мне надо.
А как вашу конструкцию прикрутить к прерываниям?
Вопрос не в этих 5 тактах. 5 тактов - если 2 режима. На самом деле режимов больше.
|
|
|
|
21.08.2010, 13:49
|
|
Почётный гражданин KAZUS.RU
Регистрация: 08.01.2008
Сообщений: 1,143
Сказал спасибо: 379
Сказали Спасибо 430 раз(а) в 274 сообщении(ях)
|
Re: Как изменить адрес подпрограммы обработки прерывания?
Вот как это может выглядеть на asm.
Цитата:
|
.device ATtiny13
.CSEG
.ORG 0
rjmp RESET ;Reset
.ORG $02
rjmp ExtIntReq ;External Interrupt Request
.ORG $06
ijmp ;Timer/Counter0 Compare Match A
.ORG INT_VECTORS_SIZE
RESET:
;инициализация вектора TimerCompareA
ldi r30,low(TimerCompareA)
ldi r31,high(TimerCompareA)
....
sei
...
Func:
......
Call send
......
rjmp Func
TimerCompareA:
nop
reti
Send:
.....
ldi r30,low(Transmite_Byte)
ldi r31,high(Transmite_Byte)
.....
ret
Transmite_Byte:
.....
ldi r30,low(Delay_Transmit)
ldi r31,high(Delay_Transmit)
reti
EndTransmite:
.......
ldi r30,low(TimerCompareA)
ldi r31,high(TimerCompareA)
reti
Delay_Transmit:
ldi r30,low(Transmite_Byte)
ldi r31,high(Transmite_Byte)
reti
.EXIT
|
При выполнении процедуры send управление прерыванием Timer0 Compare Match A передается с пустого обработчика TimerCompareA на Transmite_Byte апри завершении передачи снова на пустое прерывание.
Зачем еще тут Delay_Transmit да просто нужно было наружный сигнал поделить на 372.
|
|
|
|
21.08.2010, 16:28
|
|
Прохожий
Регистрация: 08.08.2010
Сообщений: 7
Сказал спасибо: 1
Сказали Спасибо 1 раз в 1 сообщении
|
Re: Как изменить адрес подпрограммы обработки прерывания?
Сообщение от Godzilla82
|
ISR = ISR1 - это, естесственно, неверно, просто как бы показывает, что мне надо.
А как вашу конструкцию прикрутить к прерываниям?
Вопрос не в этих 5 тактах. 5 тактов - если 2 режима. На самом деле режимов больше.
|
Да, ISR=ISR1 - это неверно, так как ISR это макрос, который потом разворачивается в функцию прерывания.
А привязка в моем примере осуществляется при помощи указателей.
мы создаем указатель который указывает на таблицу векторов прерываний.
в моем примере это
PHP код:
|
int *my_int_ptr;//указатель
|
далее инициализируем его
PHP код:
|
my_int_ptr = (int*)0;
|
, чтоб он указывал на начало таблцы прерываний.
А далее в прогамме нужному номеру назначаем нужный обработчик
PHP код:
|
my_int_ptr[2] = (int)my_int_1;//поменяли 2-й вектор
|
И еще, может вам использовать конструкцию
PHP код:
|
switch(state)
{
case 0:
//do do do
break;
.....
case n:
break;
default:
state=0
}
|
|
|
|
|
21.08.2010, 17:00
|
|
Почётный гражданин KAZUS.RU
Регистрация: 13.12.2004
Сообщений: 3,172
Сказал спасибо: 11
Сказали Спасибо 692 раз(а) в 504 сообщении(ях)
|
Re: Как изменить адрес подпрограммы обработки прерывания?
Сообщение от alex_312
|
А далее в прогамме нужному номеру назначаем нужный обработчик
|
Попобуйте в реале, удивитесь результату. Куча регистров уйдет в стек, ведь компилятор по сути вызывает обычную функцию по указателю. Которая имеет право изменять Call used регистры, что в прерывании недопустимо. Так что вместо экономии тактов 30-40 в начале уйдет на сохранение регистров.
Сообщение от alex_312
|
так как ISR это макрос, который потом разворачивается в функцию прерывания.
|
Это не в функцию разворачивается, а сего лишь аттрибуты функции присваивает.
Так что чисто на Си ничего не получится, минимум - разместить обработчики на неиспользованных векторах и переключать эти обработчики из ассемблерной обертки. И хранить указатель в регистровой переменной. Ограничение - количество свободных векторов прерывания. Впрочем можно в каждый еще ветвления вставлять. А нормально сделать получится только если обработчики будут тоже на ассемблере. Тут скорость почти максимальна. Оверхед - 8 тактов на вход и 7 на выход.
|
|
|
|
Ваши права в разделе
|
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения
HTML код Выкл.
|
|
|
Часовой пояс GMT +4, время: 03:26.
|
|