Реклама на сайте DatasheetsDatasheets

KAZUS.RUN - Электронный портал. Принципиальные схемы, Datasheets, Форум по электронике

Новости электроники Новости Литература, электронные книги Литература Документация, даташиты Документация Поиск даташитов (datasheets)Поиск PDF
  От производителей
Новости поставщиков
В мире электроники

  Сборник статей
Книги и журналы
FAQ по электронике

  Datasheets
Поиск SMD
Онлайн справочник

Принципиальные схемы Схемы Каталоги программ, сайтов Каталоги Общение, форум Общение Ваш аккаунтАккаунт
  Каталог схем
Схемы и проекты
Принципиальные схемы
  Программы
Каталог сайтов
Калькуляторы
  Форумы по электронике
Помощь проекту

Микроконтроллеры, АЦП, память и т.д Темы касающиеся микроконтроллеров разных производителей, памяти, АЦП/ЦАП, периферийных модулей...

 
Опции темы

Как изменить адрес подпрограммы обработки прерывания?

Непрочитано 21.08.2010, 17:10  
alex_312
Прохожий
 
Аватар для alex_312
 
Регистрация: 08.08.2010
Сообщений: 7
Сказал спасибо: 1
Сказали Спасибо 1 раз в 1 сообщении
alex_312 на пути к лучшему
По умолчанию Re: Как изменить адрес подпрограммы обработки прерывания?

Сообщение от kison Посмотреть сообщение
Попобуйте в реале, удивитесь результату. Куча регистров уйдет в стек, ведь компилятор по сути вызывает обычную функцию по указателю. Которая имеет право изменять Call used регистры, что в прерывании недопустимо. Так что вместо экономии тактов 30-40 в начале уйдет на сохранение регистров.


Это не в функцию разворачивается, а сего лишь аттрибуты функции присваивает.
Так что чисто на Си ничего не получится, минимум - разместить обработчики на неиспользованных векторах и переключать эти обработчики из ассемблерной обертки. И хранить указатель в регистровой переменной. Ограничение - количество свободных векторов прерывания. Впрочем можно в каждый еще ветвления вставлять. А нормально сделать получится только если обработчики будут тоже на ассемблере. Тут скорость почти максимальна. Оверхед - 8 тактов на вход и 7 на выход.
А что мне удивятся, это вы удивитесь, когда посмотрите ассемблерный листинг и увидите что любое прерывание начинается с того что сохраняются все (практически все) регистры в стек, а перед выходом из прерывания они восстанавливаются из стека.

И, кстати, про необходимость ручного сохранения/восстановления регистров я писал.
Смотрите пост №6https://kazus.run/forums/showpost.ph...09&postcount=6
Реклама:

Последний раз редактировалось alex_312; 21.08.2010 в 17:20.
alex_312 вне форума  
Непрочитано 21.08.2010, 18:34  
kison
Почётный гражданин KAZUS.RUN
 
Регистрация: 13.12.2004
Сообщений: 3,168
Сказал спасибо: 11
Сказали Спасибо 691 раз(а) в 503 сообщении(ях)
kison на пути к лучшему
По умолчанию Re: Как изменить адрес подпрограммы обработки прерывания?

Сообщение от alex_312 Посмотреть сообщение
А что мне удивятся, это вы удивитесь, когда посмотрите ассемблерный листинг и увидите что любое прерывание начинается с того что сохраняются все (практически все) регистры в стек, а перед выходом из прерывания они восстанавливаются из стека.
Я бы удивился, если бы Вы оказались правы. Но на самом деле:
Код:
uint8_t a;
volatile uint8_t result;

ISR(INT1_vect)
{
result  = a;
}
И листинг
Код:
11:       {
+0000002D:   921F        PUSH    R1               Push register on stack
+0000002E:   920F        PUSH    R0               Push register on stack
+0000002F:   B60F        IN      R0,0x3F          In from I/O location
+00000030:   920F        PUSH    R0               Push register on stack
+00000031:   2411        CLR     R1               Clear Register
+00000032:   938F        PUSH    R24              Push register on stack
12:       result  = a;
+00000033:   91800061    LDS     R24,0x0061       Load direct from data space
+00000035:   93800060    STS     0x0060,R24       Store direct to data space
+00000037:   918F        POP     R24              Pop register from stack
+00000038:   900F        POP     R0               Pop register from stack
+00000039:   BE0F        OUT     0x3F,R0          Out to I/O location
+0000003A:   900F        POP     R0               Pop register from stack
+0000003B:   901F        POP     R1               Pop register from stack
+0000003C:   9518        RETI                     Interrupt return
В стек идут 3 регистра и SREG. Три это почти все из 32?
А теперь то же, но через указатель. Т.е. то, что Вы предложили.
Код:
void Func(void);
typedef void (*FuncPtr_t)(void);

FuncPtr_t pFunc = Func;
uint8_t a;
volatile uint8_t result;

void Func(void)
{
result = a;
}

ISR(INT1_vect)
{
(*pFunc)();
}
И ее результат. Учтите, там только вызов функции, никакой полезной работы уже нет.
Код:
@00000032: __vector_2
21:       {
+00000032:   921F        PUSH    R1               Push register on stack
+00000033:   920F        PUSH    R0               Push register on stack
+00000034:   B60F        IN      R0,0x3F          In from I/O location
+00000035:   920F        PUSH    R0               Push register on stack
+00000036:   2411        CLR     R1               Clear Register
+00000037:   932F        PUSH    R18              Push register on stack
+00000038:   933F        PUSH    R19              Push register on stack
+00000039:   934F        PUSH    R20              Push register on stack
+0000003A:   935F        PUSH    R21              Push register on stack
+0000003B:   936F        PUSH    R22              Push register on stack
+0000003C:   937F        PUSH    R23              Push register on stack
+0000003D:   938F        PUSH    R24              Push register on stack
+0000003E:   939F        PUSH    R25              Push register on stack
+0000003F:   93AF        PUSH    R26              Push register on stack
+00000040:   93BF        PUSH    R27              Push register on stack
+00000041:   93EF        PUSH    R30              Push register on stack
+00000042:   93FF        PUSH    R31              Push register on stack
22:       (*pFunc)();
+00000043:   91E00060    LDS     R30,0x0060       Load direct from data space
+00000045:   91F00061    LDS     R31,0x0061       Load direct from data space
+00000047:   9509        ICALL                    Indirect call to (Z)
+00000048:   91FF        POP     R31              Pop register from stack
+00000049:   91EF        POP     R30              Pop register from stack
+0000004A:   91BF        POP     R27              Pop register from stack
+0000004B:   91AF        POP     R26              Pop register from stack
+0000004C:   919F        POP     R25              Pop register from stack
+0000004D:   918F        POP     R24              Pop register from stack
+0000004E:   917F        POP     R23              Pop register from stack
+0000004F:   916F        POP     R22              Pop register from stack
+00000050:   915F        POP     R21              Pop register from stack
+00000051:   914F        POP     R20              Pop register from stack
+00000052:   913F        POP     R19              Pop register from stack
+00000053:   912F        POP     R18              Pop register from stack
+00000054:   900F        POP     R0               Pop register from stack
+00000055:   BE0F        OUT     0x3F,R0          Out to I/O location
+00000056:   900F        POP     R0               Pop register from stack
+00000057:   901F        POP     R1               Pop register from stack
+00000058:   9518        RETI                     Interrupt return
В стек идут уже 14 регистров + SREG. 11 лишних, по два такта на PUSH - 22 лишних такта. А тут топикстартер из за 5 лишних расстраивается.
kison вне форума  
Непрочитано 21.08.2010, 21:27  
testerplus
Прописка
 
Регистрация: 26.01.2009
Сообщений: 249
Сказал спасибо: 23
Сказали Спасибо 102 раз(а) в 61 сообщении(ях)
testerplus на пути к лучшему
По умолчанию Re: Как изменить адрес подпрограммы обработки прерывания?

Сообщение от Godzilla82 Посмотреть сообщение
У меня как раз такая задача, когда всё упирается в эти самые лишние 3-5 тактов. Нужно по адресу вектора прерывания поменять адрес перехода на нужную подпрограмму. Думаю, что это возможно сделать средствами СИ (пусть даже с ассемблерными вставками). Поэтому и спрашиваю.
А не рассматривается вариант с двумя таймерами? В одном режиме разрешено прерывание от одного таймера, в другом - от другого. Или на одном таймере, но использовать источники прерываний 1) по переполнению, 2) по compare
testerplus вне форума  
Непрочитано 22.08.2010, 01:27  
nml
Супер-модератор
 
Аватар для nml
 
Регистрация: 13.03.2004
Адрес: Minsk
Сообщений: 2,394
Сказал спасибо: 2,016
Сказали Спасибо 1,356 раз(а) в 591 сообщении(ях)
nml на пути к лучшему
По умолчанию Re: Как изменить адрес подпрограммы обработки прерывания?

Сообщение от Godzilla82 Посмотреть сообщение
Например, есть два режима работы контроллера. В одном режиме по прерыванию от таймера должны выполняться одни действия. В другом режиме - по этому же прерыванию - другие действия.
Вектор изменить нельзя. Напишите в начале обработчика
Код:
if (mode) {сделать то-то} else {сделать нетото}
А такты экономьте за счет алгоритма и тонкостей компилятора.
__________________
[ жизнь приятна и красива, если выпить литр пива ]
nml вне форума  
Непрочитано 22.08.2010, 05:00  
Godzilla82
Почётный гражданин KAZUS.RUN
 
Регистрация: 29.10.2006
Сообщений: 1,449
Сказал спасибо: 99
Сказали Спасибо 318 раз(а) в 234 сообщении(ях)
Godzilla82 на пути к лучшему
Сообщение Re: Как изменить адрес подпрограммы обработки прерывания?

Сообщение от testerplus Посмотреть сообщение
А не рассматривается вариант ...
Сообщение от nml Посмотреть сообщение
...такты экономьте за счет алгоритма и тонкостей компилятора.
Так и придётся. Думал, что есть простое и красивое решение. Заменил вектор - и вуаля

Подумываю о таком финте:
PHP код:
int adr;

void proc1(void)
{
...
}

void proc2(void)
{
...
}

ISR(...)
{
call adr;
}

void main(void)
{
...
if(uslovie) adr = proc1;
else        adr = proc2;
} 
Понятно, что это и не СИ, и не ассемблер, но свою мысль я донёс. Как такое сделать?

Последний раз редактировалось Godzilla82; 22.08.2010 в 05:04.
Godzilla82 вне форума  
Непрочитано 22.08.2010, 13:46  
kison
Почётный гражданин KAZUS.RUN
 
Регистрация: 13.12.2004
Сообщений: 3,168
Сказал спасибо: 11
Сказали Спасибо 691 раз(а) в 503 сообщении(ях)
kison на пути к лучшему
По умолчанию Re: Как изменить адрес подпрограммы обработки прерывания?

Сообщение от Godzilla82 Посмотреть сообщение
Понятно, что это и не СИ, и не ассемблер, но свою мысль я донёс. Как такое сделать?
В 8 сообщении рабочий пример на ассемблере. Причем без оверхеда, скорость почти максимум. 1 такт лишний всего если вектора под RJMP и вообще без потерь если вектора под JMP.
Недостаток один - только ассемблер.
В 12 сообщении пример с вызовом функции по указателю из прерывания. Достоинство - чистый Си. Недостаток - жуткие тормоза. Там указатель на функцию pFunc, присваивая ему разные адреса функций можно переназначать обработчик. Причины тормознутости в 10 сообщении.
Это все и есть тот финт, о котором подумываете.
kison вне форума  
Непрочитано 22.08.2010, 14:04  
Godzilla82
Почётный гражданин KAZUS.RUN
 
Регистрация: 29.10.2006
Сообщений: 1,449
Сказал спасибо: 99
Сказали Спасибо 318 раз(а) в 234 сообщении(ях)
Godzilla82 на пути к лучшему
Сообщение Re: Как изменить адрес подпрограммы обработки прерывания?

Сообщение от kison Посмотреть сообщение
В 8 сообщении рабочий пример на ассемблере. Причем без оверхеда, скорость почти максимум. 1 такт лишний всего если вектора под RJMP и вообще без потерь если вектора под JMP.
Недостаток один - только ассемблер.
Ассемблер для меня пока не вариант. Думаю, и в будущем - тоже.

Сообщение от kison Посмотреть сообщение
В 12 сообщении пример с вызовом функции по указателю из прерывания. Достоинство - чистый Си. Недостаток - жуткие тормоза.
В том-то и дело, что жуткие... А если использовать вызов процедуры по ассемблерной вставке? Получится, что некоторые используемые регистры не будут помещены в стек?

В общем, чё-то это всё на Си совсем грустно выглядит...
Godzilla82 вне форума  
Непрочитано 22.08.2010, 14:24  
kison
Почётный гражданин KAZUS.RUN
 
Регистрация: 13.12.2004
Сообщений: 3,168
Сказал спасибо: 11
Сказали Спасибо 691 раз(а) в 503 сообщении(ях)
kison на пути к лучшему
По умолчанию Re: Как изменить адрес подпрограммы обработки прерывания?

Сообщение от Godzilla82 Посмотреть сообщение
В общем, чё-то это всё на Си совсем грустно выглядит...
Это может и весело выглядеть. Оказывается есть вариант, но 8 тактов на входе он отъест.
Во всяком случае в WinAvr срабатывает. Можно имя вектора указывать произвольно. Не только ISR(INT1_vect), но и ISR(SuperVect) проходит. Компилятор честно создает обработчик, правда варнинг выдает что вектора для него нет, но с этим придется смириться. Так что количество обработчиков неограниченно
Смотрите 4-е сообщение, там алгоритм расписан. Единственное - после icall нужно снова запретить прерывания, и восстановить из стека R30 и R31, затем reti. А может cli и не нужно, надо обдумать это. SREG в обертке сохранять не нужно.

Последний раз редактировалось kison; 22.08.2010 в 14:32.
kison вне форума  
Сказали "Спасибо" kison
Godzilla82 (22.08.2010)
Непрочитано 22.08.2010, 17:50  
alex_312
Прохожий
 
Аватар для alex_312
 
Регистрация: 08.08.2010
Сообщений: 7
Сказал спасибо: 1
Сказали Спасибо 1 раз в 1 сообщении
alex_312 на пути к лучшему
По умолчанию Re: Как изменить адрес подпрограммы обработки прерывания?

Сообщение от kison Посмотреть сообщение
Я бы удивился, если бы Вы оказались правы. Но на самом деле:
Код:
uint8_t a;
volatile uint8_t result;

ISR(INT1_vect)
{
result  = a;
}
И листинг
Код:
11:       {
+0000002D:   921F        PUSH    R1               Push register on stack
+0000002E:   920F        PUSH    R0               Push register on stack
+0000002F:   B60F        IN      R0,0x3F          In from I/O location
+00000030:   920F        PUSH    R0               Push register on stack
+00000031:   2411        CLR     R1               Clear Register
+00000032:   938F        PUSH    R24              Push register on stack
12:       result  = a;
+00000033:   91800061    LDS     R24,0x0061       Load direct from data space
+00000035:   93800060    STS     0x0060,R24       Store direct to data space
+00000037:   918F        POP     R24              Pop register from stack
+00000038:   900F        POP     R0               Pop register from stack
+00000039:   BE0F        OUT     0x3F,R0          Out to I/O location
+0000003A:   900F        POP     R0               Pop register from stack
+0000003B:   901F        POP     R1               Pop register from stack
+0000003C:   9518        RETI                     Interrupt return
В стек идут 3 регистра и SREG. Три это почти все из 32?
А теперь то же, но через указатель. Т.е. то, что Вы предложили.
Код:
void Func(void);
typedef void (*FuncPtr_t)(void);

FuncPtr_t pFunc = Func;
uint8_t a;
volatile uint8_t result;

void Func(void)
{
result = a;
}

ISR(INT1_vect)
{
(*pFunc)();
}
И ее результат. Учтите, там только вызов функции, никакой полезной работы уже нет.
Код:
@00000032: __vector_2
21:       {
+00000032:   921F        PUSH    R1               Push register on stack
+00000033:   920F        PUSH    R0               Push register on stack
+00000034:   B60F        IN      R0,0x3F          In from I/O location
+00000035:   920F        PUSH    R0               Push register on stack
+00000036:   2411        CLR     R1               Clear Register
+00000037:   932F        PUSH    R18              Push register on stack
+00000038:   933F        PUSH    R19              Push register on stack
+00000039:   934F        PUSH    R20              Push register on stack
+0000003A:   935F        PUSH    R21              Push register on stack
+0000003B:   936F        PUSH    R22              Push register on stack
+0000003C:   937F        PUSH    R23              Push register on stack
+0000003D:   938F        PUSH    R24              Push register on stack
+0000003E:   939F        PUSH    R25              Push register on stack
+0000003F:   93AF        PUSH    R26              Push register on stack
+00000040:   93BF        PUSH    R27              Push register on stack
+00000041:   93EF        PUSH    R30              Push register on stack
+00000042:   93FF        PUSH    R31              Push register on stack
22:       (*pFunc)();
+00000043:   91E00060    LDS     R30,0x0060       Load direct from data space
+00000045:   91F00061    LDS     R31,0x0061       Load direct from data space
+00000047:   9509        ICALL                    Indirect call to (Z)
+00000048:   91FF        POP     R31              Pop register from stack
+00000049:   91EF        POP     R30              Pop register from stack
+0000004A:   91BF        POP     R27              Pop register from stack
+0000004B:   91AF        POP     R26              Pop register from stack
+0000004C:   919F        POP     R25              Pop register from stack
+0000004D:   918F        POP     R24              Pop register from stack
+0000004E:   917F        POP     R23              Pop register from stack
+0000004F:   916F        POP     R22              Pop register from stack
+00000050:   915F        POP     R21              Pop register from stack
+00000051:   914F        POP     R20              Pop register from stack
+00000052:   913F        POP     R19              Pop register from stack
+00000053:   912F        POP     R18              Pop register from stack
+00000054:   900F        POP     R0               Pop register from stack
+00000055:   BE0F        OUT     0x3F,R0          Out to I/O location
+00000056:   900F        POP     R0               Pop register from stack
+00000057:   901F        POP     R1               Pop register from stack
+00000058:   9518        RETI                     Interrupt return
В стек идут уже 14 регистров + SREG. 11 лишних, по два такта на PUSH - 22 лишних такта. А тут топикстартер из за 5 лишних расстраивается.
уважаемый кисон, вы превратно поняли смысл моего предложения, я НИГДЕ не предлагал в обработчике прерывания вызывать функцию через указатель - это раз.

А во вторых, исходники которые вы продемонстрировали ровным счетом ничего не показывают (кроме нормальной работы оптимизатора), я думаю если бы у торикстартера в прерывании выполнялось простое присваивание то не стоит городить огород.

Мое решение основано на том, что нам известен адрес вектора, и я показал как его поменять.
alex_312 вне форума  
Непрочитано 22.08.2010, 17:51  
alex_312
Прохожий
 
Аватар для alex_312
 
Регистрация: 08.08.2010
Сообщений: 7
Сказал спасибо: 1
Сказали Спасибо 1 раз в 1 сообщении
alex_312 на пути к лучшему
По умолчанию Re: Как изменить адрес подпрограммы обработки прерывания?

Сообщение от nml Посмотреть сообщение
Вектор изменить нельзя. Напишите в начале обработчика
Код:
if (mode) {сделать то-то} else {сделать нетото}
А такты экономьте за счет алгоритма и тонкостей компилятора.
И чего же такого святого в векторе прерывания?
alex_312 вне форума  
 
Опции темы

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход

Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
[Решено] Как внедрялась АСУ chermnv Отвлекитесь, эмбеддеры! 743 14.02.2010 23:11
[Решено] Третье Послание Человечеству lelkanet Отвлекитесь, эмбеддеры! 201 06.01.2010 00:33
Вход в прерывания для PIC микроконтроллеров dimmich Микроконтроллеры, АЦП, память и т.д 4 06.12.2009 17:17
Keil C51 как изменить адрес загрузки Kabron Микроконтроллеры, АЦП, память и т.д 4 09.10.2009 23:31


Часовой пояс GMT +4, время: 08:33.


Powered by vBulletin® Version 3.8.4
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.