Микроконтроллеры, АЦП, память и т.д Темы касающиеся микроконтроллеров разных производителей, памяти, АЦП/ЦАП, периферийных модулей... |
21.08.2010, 17:10
|
|
Прохожий
Регистрация: 08.08.2010
Сообщений: 7
Сказал спасибо: 1
Сказали Спасибо 1 раз в 1 сообщении
|
Re: Как изменить адрес подпрограммы обработки прерывания?
Сообщение от kison
|
Попобуйте в реале, удивитесь результату. Куча регистров уйдет в стек, ведь компилятор по сути вызывает обычную функцию по указателю. Которая имеет право изменять Call used регистры, что в прерывании недопустимо. Так что вместо экономии тактов 30-40 в начале уйдет на сохранение регистров.
Это не в функцию разворачивается, а сего лишь аттрибуты функции присваивает.
Так что чисто на Си ничего не получится, минимум - разместить обработчики на неиспользованных векторах и переключать эти обработчики из ассемблерной обертки. И хранить указатель в регистровой переменной. Ограничение - количество свободных векторов прерывания. Впрочем можно в каждый еще ветвления вставлять. А нормально сделать получится только если обработчики будут тоже на ассемблере. Тут скорость почти максимальна. Оверхед - 8 тактов на вход и 7 на выход.
|
А что мне удивятся, это вы удивитесь, когда посмотрите ассемблерный листинг и увидите что любое прерывание начинается с того что сохраняются все (практически все) регистры в стек, а перед выходом из прерывания они восстанавливаются из стека.
И, кстати, про необходимость ручного сохранения/восстановления регистров я писал.
Смотрите пост №6 https://kazus.ru/forums/showpost.php...09&postcount=6
Последний раз редактировалось alex_312; 21.08.2010 в 17:20.
|
|
|
|
21.08.2010, 18:34
|
|
Почётный гражданин KAZUS.RU
Регистрация: 13.12.2004
Сообщений: 3,172
Сказал спасибо: 11
Сказали Спасибо 692 раз(а) в 504 сообщении(ях)
|
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? ![Улыбка](images/smilies/icon_smile.gif)
А теперь то же, но через указатель. Т.е. то, что Вы предложили.
Код:
|
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 лишних расстраивается.
|
|
|
|
21.08.2010, 21:27
|
|
Прописка
Регистрация: 26.01.2009
Сообщений: 249
Сказал спасибо: 23
Сказали Спасибо 102 раз(а) в 61 сообщении(ях)
|
Re: Как изменить адрес подпрограммы обработки прерывания?
Сообщение от Godzilla82
|
У меня как раз такая задача, когда всё упирается в эти самые лишние 3-5 тактов. Нужно по адресу вектора прерывания поменять адрес перехода на нужную подпрограмму. Думаю, что это возможно сделать средствами СИ (пусть даже с ассемблерными вставками). Поэтому и спрашиваю.
|
А не рассматривается вариант с двумя таймерами? В одном режиме разрешено прерывание от одного таймера, в другом - от другого. Или на одном таймере, но использовать источники прерываний 1) по переполнению, 2) по compare
|
|
|
|
22.08.2010, 01:27
|
|
Супер-модератор
Регистрация: 13.03.2004
Адрес: Minsk
Сообщений: 2,378
Сказал спасибо: 1,956
Сказали Спасибо 1,328 раз(а) в 578 сообщении(ях)
|
Re: Как изменить адрес подпрограммы обработки прерывания?
Сообщение от Godzilla82
|
Например, есть два режима работы контроллера. В одном режиме по прерыванию от таймера должны выполняться одни действия. В другом режиме - по этому же прерыванию - другие действия.
|
Вектор изменить нельзя. Напишите в начале обработчика
Код:
|
if (mode) {сделать то-то} else {сделать нетото} |
А такты экономьте за счет алгоритма и тонкостей компилятора.
__________________
[ жизнь приятна и красива, если выпить литр пива ]
|
|
|
|
22.08.2010, 05:00
|
|
Почётный гражданин KAZUS.RU
Регистрация: 29.10.2006
Сообщений: 1,446
Сказал спасибо: 99
Сказали Спасибо 317 раз(а) в 233 сообщении(ях)
|
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.
|
|
|
|
22.08.2010, 13:46
|
|
Почётный гражданин KAZUS.RU
Регистрация: 13.12.2004
Сообщений: 3,172
Сказал спасибо: 11
Сказали Спасибо 692 раз(а) в 504 сообщении(ях)
|
Re: Как изменить адрес подпрограммы обработки прерывания?
Сообщение от Godzilla82
|
Понятно, что это и не СИ, и не ассемблер, но свою мысль я донёс. Как такое сделать?
|
В 8 сообщении рабочий пример на ассемблере. Причем без оверхеда, скорость почти максимум. 1 такт лишний всего если вектора под RJMP и вообще без потерь если вектора под JMP.
Недостаток один - только ассемблер.
В 12 сообщении пример с вызовом функции по указателю из прерывания. Достоинство - чистый Си. Недостаток - жуткие тормоза. Там указатель на функцию pFunc, присваивая ему разные адреса функций можно переназначать обработчик. Причины тормознутости в 10 сообщении.
Это все и есть тот финт, о котором подумываете.
|
|
|
|
22.08.2010, 14:04
|
|
Почётный гражданин KAZUS.RU
Регистрация: 29.10.2006
Сообщений: 1,446
Сказал спасибо: 99
Сказали Спасибо 317 раз(а) в 233 сообщении(ях)
|
Re: Как изменить адрес подпрограммы обработки прерывания?
Сообщение от kison
|
В 8 сообщении рабочий пример на ассемблере. Причем без оверхеда, скорость почти максимум. 1 такт лишний всего если вектора под RJMP и вообще без потерь если вектора под JMP.
Недостаток один - только ассемблер.
|
Ассемблер для меня пока не вариант. Думаю, и в будущем - тоже.
Сообщение от kison
|
В 12 сообщении пример с вызовом функции по указателю из прерывания. Достоинство - чистый Си. Недостаток - жуткие тормоза.
|
В том-то и дело, что жуткие... А если использовать вызов процедуры по ассемблерной вставке? Получится, что некоторые используемые регистры не будут помещены в стек?
В общем, чё-то это всё на Си совсем грустно выглядит...
|
|
|
|
22.08.2010, 14:24
|
|
Почётный гражданин KAZUS.RU
Регистрация: 13.12.2004
Сообщений: 3,172
Сказал спасибо: 11
Сказали Спасибо 692 раз(а) в 504 сообщении(ях)
|
Re: Как изменить адрес подпрограммы обработки прерывания?
Сообщение от Godzilla82
|
В общем, чё-то это всё на Си совсем грустно выглядит...
|
Это может и весело выглядеть. Оказывается есть вариант, но 8 тактов на входе он отъест.
Во всяком случае в WinAvr срабатывает. Можно имя вектора указывать произвольно. Не только ISR(INT1_vect), но и ISR(SuperVect) проходит. Компилятор честно создает обработчик, правда варнинг выдает что вектора для него нет, но с этим придется смириться. Так что количество обработчиков неограниченно ![Улыбка](images/smilies/icon_smile.gif)
Смотрите 4-е сообщение, там алгоритм расписан. Единственное - после icall нужно снова запретить прерывания, и восстановить из стека R30 и R31, затем reti. А может cli и не нужно, надо обдумать это. SREG в обертке сохранять не нужно.
Последний раз редактировалось kison; 22.08.2010 в 14:32.
|
|
|
|
22.08.2010, 17:50
|
|
Прохожий
Регистрация: 08.08.2010
Сообщений: 7
Сказал спасибо: 1
Сказали Спасибо 1 раз в 1 сообщении
|
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? ![Улыбка](images/smilies/icon_smile.gif)
А теперь то же, но через указатель. Т.е. то, что Вы предложили.
Код:
|
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 лишних расстраивается.
|
уважаемый кисон, вы превратно поняли смысл моего предложения, я НИГДЕ не предлагал в обработчике прерывания вызывать функцию через указатель - это раз.
А во вторых, исходники которые вы продемонстрировали ровным счетом ничего не показывают (кроме нормальной работы оптимизатора), я думаю если бы у торикстартера в прерывании выполнялось простое присваивание то не стоит городить огород.
Мое решение основано на том, что нам известен адрес вектора, и я показал как его поменять.
|
|
|
|
22.08.2010, 17:51
|
|
Прохожий
Регистрация: 08.08.2010
Сообщений: 7
Сказал спасибо: 1
Сказали Спасибо 1 раз в 1 сообщении
|
Re: Как изменить адрес подпрограммы обработки прерывания?
Сообщение от nml
|
Вектор изменить нельзя. Напишите в начале обработчика
Код:
|
if (mode) {сделать то-то} else {сделать нетото} |
А такты экономьте за счет алгоритма и тонкостей компилятора.
|
И чего же такого святого в векторе прерывания?
|
|
|
|
Ваши права в разделе
|
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения
HTML код Выкл.
|
|
|
Часовой пояс GMT +4, время: 23:30.
|
|