реклама на сайте
подробности

 
 
3 страниц V  < 1 2 3 >  
Reply to this topicStart new topic
> Замена обработчика прерывания в WinAVR
singlskv
сообщение Dec 16 2008, 18:44
Сообщение #16


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Господа, Вы занимаетесь просто какой-то х.р..й ... smile.gif smile.gif smile.gif
Вот это обертка в асм файле isr.s :
Код
.extern curr_isr
.global TIMER1_COMPA_vect; прерывание с переключаемым обработчиком
TIMER1_COMPA_vect:
  push  r30
  push  r31
  lds   r30, curr_isr
  lds   r31, curr_isr + 1
  icall
  pop   r31
  pop   r30
  reti


а вот это сам файл с обработчиками и примером:
Код
#include <avr/io.h>
#include <avr/interrupt.h>

volatile unsigned char tmp;

void INT0_vect(void) __attribute__((interrupt)); // свободный вектор прерывания
void INT0_vect(void)
{
  tmp = 0; // тело обработчика 0
}

void INT1_vect(void) __attribute__((interrupt)); // свободный вектор прерывания
void INT1_vect(void)
{
  tmp = 1; // тело обработчика 1
}

//Указатель на функцию обработчик
void (* curr_isr)(void) = INT1_vect;

int main(void)
{
  curr_isr = INT1_vect;
  asm("sei");
  while (1);
}

Накладные расходы примерно 16МЦ... на фсе...
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Dec 16 2008, 19:50
Сообщение #17


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Цитата(singlskv @ Dec 16 2008, 22:44) *
Господа, Вы занимаетесь просто какой-то х.р..й ... smile.gif smile.gif smile.gif

Кому х.р.я, а кому - сын ошибок трудных. smile.gif
Т.е. получается, что __attribute__((interrupt)) и заставляет все call-used регистры сохранять внутри функции?
Цитата
Кстати Ваш ужас ...

Как приятно иногда сказать: знатоки, блин. lol.gif
Go to the top of the page
 
+Quote Post
singlskv
сообщение Dec 16 2008, 20:03
Сообщение #18


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(_Pasha @ Dec 16 2008, 22:50) *
Т.е. получается, что __attribute__((interrupt)) и заставляет все call-used регистры сохранять внутри функции?
Справедливости ради, такой подход имеет один побочный эфект,
прерывания будут разрешены еще до окончательного выхода из нашего
главного обработчика, НО, если прерывания идут с таким темпом что это
может вызвать переполнение стека, при других вариантах обработки мы просто
будем пропускать прерывания, так что такой вариант ИМХО вполне адекватный...
Go to the top of the page
 
+Quote Post
733259
сообщение Dec 17 2008, 02:01
Сообщение #19


Местный
***

Группа: Участник
Сообщений: 205
Регистрация: 8-03-05
Пользователь №: 3 146



Цитата
Справедливости ради, такой подход имеет один побочный эфект, прерывания будут разрешены еще до окончательного выхода из нашего главного обработчика
С __attribute__((interrupt)) прерывания будут разрешены сразу после входа в INT0_vect (INT0_vect), первой командой будет sei. C __attribute__((signal)) такого не будет, ИМХО правильнее __attribute__((signal,used)). К тому же вариант singlskv не универсален - переключаются векторы INT0, INT1, а если они нужны? В случае "просто" функций - компилятор выдаст предупреждение "misspelled interrupt handler" - из-за этого данный вариант мне показался не подходящим.
ИМХО в реале накладные расходы на if будут меньше push r30, icall и т.д.
Цитата
Как приятно иногда сказать: знатоки, блин.
Не совсем понял иронию.
Go to the top of the page
 
+Quote Post
Rst7
сообщение Dec 17 2008, 07:42
Сообщение #20


Йа моск ;)
******

Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610



Не знаю, можно ли все, что я тут напишу, реализовать в GCC, но в IAR'е я бы делал так
Код
static void (*IntProc_Pointer)(void);
static UREG save_r16;

#pragma diag_suppress=Ta006
__interrupt void Proc1(void)
{
  extern void foo(UINT16);
  foo(1234);
}

__interrupt void Proc2(void)
{
  extern void foo(UINT16);
  foo(4566);
}

#pragma diag_default=Ta006


#pragma required=save_r16
#pragma required=IntProc_Pointer

#pragma vector=PCINT0_vect
__raw __interrupt void Proc_JMP(void)
{
  asm(
"    STS    save_r16,R16\n"
"    LDS    R16,IntProc_Pointer+0\n" //Возможно, необходимо поменять местами +0 и +1
"    PUSH    R16\n"
"    LDS    R16,IntProc_Pointer+1\n"
"    PUSH    R16\n"
"    LDS    R16,save_r16\n"
"    RET\n"
);

}

__monitor void Write_IntProc_Pointer(void(__interrupt *p)(void))
{
  IntProc_Pointer=(void(*)(void))p;
}

void Set_Proc1(void)
{
  Write_IntProc_Pointer(Proc1);
  
}

void Set_Proc2(void)
{
  Write_IntProc_Pointer(Proc2);
  
}


Так меньше всего стека уйдет.


--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
Go to the top of the page
 
+Quote Post
733259
сообщение Dec 17 2008, 07:55
Сообщение #21


Местный
***

Группа: Участник
Сообщений: 205
Регистрация: 8-03-05
Пользователь №: 3 146



Что Ваш вариант в 20 тактов, что через icall в 21.
Тогда как банальный if(test) - 5 или 6 тактов - lds, and, breq.
ИМХО для 2 функций ни к чему. Навороты хороши не тогда, когда в комлекте ещё тормоза.
Go to the top of the page
 
+Quote Post
Rst7
сообщение Dec 17 2008, 08:19
Сообщение #22


Йа моск ;)
******

Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610



Цитата
Что Ваш вариант в 20 тактов, что через icall в 21.


Дык я про то, что в моем варианте лишний стек не используется. По скорости это конечно тормоза.

Цитата
ИМХО для 2 функций ни к чему.


А если их 22?


--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Dec 17 2008, 10:17
Сообщение #23


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Цитата(733259 @ Dec 17 2008, 06:01) *
правильнее __attribute__((signal,used)). К тому же вариант singlskv не универсален - переключаются векторы INT0, INT1, а если они нужны?

Следовательно, как компромисс, можно просто заюзать все неиспользуемые вектора.
Есть у меня еще в стадии тестирования субвекторизация. Допустим, у нас есть функция, при должном внимании к построению которой можно добиться, что она состоит из набора независимых блоков, между которыми компилятор не будет осуществлять операции по сохранению используемых регистров
Код
void foo(void)
{
{
   block 1
}
//........
{
   block N
}
}


Сделаем необходимый инструментарий
Код
typedef uint16_t pc_type; // пока обошел указатели стороной

uint8_t wherePC(void* pcreg) __attribute__((naked));
void setPC(pc_type newPC) __attribute__((naked));
/*Обертка для субвекторов*/
#define SubVector(t_name) if(wherePC(&t_name))


WherePC заточено таким образом, чтобы возвращать 0 при вызове, а в параметре - адрес, с которого ее вызывали, исключая обработку возвращаемого значения.
Код
void setPC(pc_type newPC)
{
asm volatile("ijmp" : : "z" (newPC));
}

uint8_t wherePC(void* pcreg)                      
{
asm volatile (
          "pop        r25"    "\n\t"
        "pop        r24"    "\n\t"
        "adiw    r24,2"    "\n\t"
        "st        Z,    r24"    "\n\t"
        "std    Z+1,r25"    "\n\t"
        "movw    r30,r24"    "\n\t"
        "sbiw    r30,2"    "\n\t"
        "clr r24"    "\n\t"
        "ijmp"                "\n\t"
    ::"z" (pcreg));
}


Используя SubVector(имя_переменной_указателя_на_блок)
Код
void foo(void)
{
static pc_type sv[3];
static uint8_t work = 0;
if(work)
{// код, использующий косв. переход на sv[n]
  set_PC(sv[1]);
}
else
{
  SubVector(sv[0])
  {
    block 1
  }
  SubVector(sv[1])
  {
    block 2
  }
  SubVector(sv[2])
  {
   block 3
  }
  work = 1;
}
return;
}


В итоге при первом вызове получаем адреса блоков кода в переменных sv[], но сами блоки не выполняем, по аналогии с setjmp, а при последующих вызовах - переходим в кратчайшие сроки к независимому блоку. В данном контексте это должно обеспечить выигрыш по сравнению со switch()

Просьба потестить эти примитивы и высказать мнения о применимости. Заранее спасибо.
Go to the top of the page
 
+Quote Post
singlskv
сообщение Dec 17 2008, 11:09
Сообщение #24


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(_Pasha @ Dec 17 2008, 13:17) *
Следовательно, как компромисс, можно просто заюзать все неиспользуемые вектора.
Конечно там нужен signal а не interrupt.
На самом деле можно заюзать и несуществующие вектора,
и с учетом варианта обертки от Rst7 можно сделать вот так:
обертка в асм файле isr.s :
Код
#include <avr/interrupt.h>
.extern curr_isr
.extern save_r16
.global TIMER1_COMPA_vect; прерывание с переключаемым обработчиком
TIMER1_COMPA_vect:
  sts   save_r16, r16
  lds   r16, curr_isr
  push  r16
  lds   r16, curr_isr + 1
  push  r16
  lds   r16, save_r16
  ret

а вот это сам файл с обработчиками и примером:
Код
#include <avr/io.h>
#include <avr/interrupt.h>

#define Isr1 __vector_Isr1
#define Isr2 __vector_Isr2

void Isr1(void) __attribute__((signal));
void Isr1(void)
{
  // тело обработчика 0
}

void Isr2(void) __attribute__((signal));
void Isr2(void)
{
  // тело обработчика 1
}

// указатель на функцию обработчик
void (* curr_isr)(void) = Isr1;
// для сохранения r16 в обработчике
unsigned char save_r16;

// установить обработчик
void Set_ISR(void (* p)(void))
{
  unsigned char sreg = SREG;
  cli();
  curr_isr = p;
  SREG = sreg;
}

int main(void)
{
  sei();

  Set_ISR(Isr1);
  Set_ISR(Isr2);

  while (1);
}
Go to the top of the page
 
+Quote Post
MrYuran
сообщение Dec 17 2008, 11:34
Сообщение #25


Беспросветный оптимист
******

Группа: Свой
Сообщений: 4 640
Регистрация: 26-12-07
Из: Н.Новгород
Пользователь №: 33 646



Цитата(Qwertty @ Dec 16 2008, 11:36) *
Вызов функции по указателю внутри обработчика ведет к сохранению ВСЕХ регистров в стеке. И восстановлению их при выходе. Не быстрый обработчик получается sad.gif

а что если "раздеть" обработчик (__naked__), а на подставляемые функции наоборот, навесить сохранение контекста?


--------------------
Программирование делится на системное и бессистемное. ©Моё :)
— а для кого-то БГ — это Bill Gilbert =)
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Dec 17 2008, 11:46
Сообщение #26


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Цитата(MrYuran @ Dec 17 2008, 15:34) *
а что если "раздеть" обработчик (__naked__), а на подставляемые функции наоборот, навесить сохранение контекста?

Не, там надо подставляемую функцию, к которой будут по указателю обращаться, делать (__naked__), а в нее заворачивать уже то, что нужно. С проктологической точки зрения - нормально smile.gif двойной вложенный вызов. А с другой стороны оптимизация может выкинуть лишний call/ret.. надо попробовать
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Dec 17 2008, 14:31
Сообщение #27


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



Цитата(_Pasha @ Dec 17 2008, 13:46) *
Не, там надо подставляемую функцию, к которой будут по указателю обращаться, делать (__naked__),
И потерять резервирование места под локальные переменные.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
defunct
сообщение Dec 20 2008, 01:03
Сообщение #28


кекс
******

Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326



Цитата(PhX @ Dec 16 2008, 11:19) *
Ну хоть как-то. Не очень хорошо конечно, но что делать. Может есть способ лучше?
Правда, в моем случае, быстродействия достаточно.

На мой взгляд, это отличный способ. Почему я так думаю - потому что:
1. Сохранение и восстановление всех регистров это всего лишь 128 тактов вход-выход в обработчик.
2. Как правило, необходимость в подмене обработчика прерывания происходит тогда, когда кардинально что-то меняется в работе программы, например - замена протокола обмена. В таких случаях логично предположить, что подменяемые обработчики "тяжеловесны" и по времени выполнения занимают гораздо больше 128 тактов.
3. Не надо гнаться за скоростью там где ее хватает, тем более в ущерб переносимости и читаемости кода.

Из чего делаем вывод - всего за 128 тактов получаем простой, прозрачный (легко читаемый), относительно безопасный, и прекрасно портируемый код на любую платформу.
Хорошо это, не очень хорошо, или совсем плохо - судить Вам ;>



Правда есть еще один тупой и топорный способ, о котором почему-то забыли упомянуть, хотя он обладает еще таким свойством как "абсолютная" безопасность smile.gif :
Код
ISR()
{
    prefix stuff

    if (...)
      handler1();
    else if (..)
      handler2();
    else
      common_handler();

   postfix stuff
}

при "сказочной" тупизне и кажущейся неоптимальности, вот какими плюсами сие обладает:
- Никакого гемороя с ассемблером.
- Тривиальная простота.
- Абсолютная безопастность - есть уверенность, что из обработчика выйдем всегда, и всегда выйдем правильно, нет риска неверного указателя в RAM.
- Возможность не только "подменять" обработчик, но и "параллельно" выполнять несколько обработчиков (например, по одному интерфейсу обслуживать несколько протоколов обмена).
- Легко портируемый.
- Нативно оптимизируемый - если все функции handler1, 2, и т.п. объявить в одном файле с обработчиком как static, то результирующий код получится эффективным - т.к. оптимизатор сделает свое дело сам.



Все шаманства с ассемблером и оптимизации "ради оптимизации" - это Зло. Лучше делать упор на функциональность, портируемость, читаемость. А вот когда действительно не будет хватать производительности и нет возможности поставить более шустрый кварц / камень, тогда (и только тогда) можно прибегнуть к извращениям на асм'е.
Go to the top of the page
 
+Quote Post
ReAl
сообщение Dec 20 2008, 16:00
Сообщение #29


Нечётный пользователь.
******

Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417



Цитата(_Pasha @ Dec 17 2008, 12:17) *
Есть у меня еще в стадии тестирования субвекторизация.
Есть несколько менее переносимый, зато более эффективный способ. В том смысле, что переписать эту субвекторизацию на другой асм другого процессора+компилятора можно, как можно и посмотреть реализацию структуры jump_buf и сделать переключатель, как у Rst7, привязанный к процессору+компилятору, но переписываемый под любую другую компбинацию. А тут - привязка к конкретному инструменту. Но раз в примере всё равно использован gcc, то... в нём можно взять адрес метки.
Во-первых, при этом можно WherePC сделать макросом, адрес вычисляется линкером:
Код
#define glue2(a,b) a##b
#define WPC_LABEL(a,b) glue2(a,b)

#define WherePC(a) { a = &&WPC_LABEL(WPC,__LINE__); WPC_LABEL(WPC,__LINE__):; }

volatile unsigned char a;

void *p1;
void *p2;

void foo() {
    ++a;
    WherePC(p1);
    ++a;
    WherePC(p2);
    ++a;
}
И результат компиляции:
Код
foo:
    lds r24,a
    subi r24,lo8(-(1))
    sts a,r24
    ldi r24,lo8(gs(.L2))
    ldi r25,hi8(gs(.L2))
    sts (p1)+1,r25
    sts p1,r24
.L2:
    lds r24,a
    subi r24,lo8(-(1))
    sts a,r24
    ldi r24,lo8(gs(.L3))
    ldi r25,hi8(gs(.L3))
    sts (p2)+1,r25
    sts p2,r24
.L3:
    lds r24,a
    subi r24,lo8(-(1))
    sts a,r24
    ret

Во вторых, этот макрос при такой постановке вопроса не нужен вообще:
http://forum.sources.ru/index.php?showtopi...p;#entry1929594

В переводе на AVR-ский язык
Код
ISR(INT0_vect)
{
    static void *next_state = &&start;

    goto *next_state;

  start:
    next_state = &&idle;
    return;

  idle:
    if (PINB & 0x01) {
        PORTB |= 0x80;
        next_state = &&drain;
    }
    return;

  drain:
    if (PINB & 0x02) {
        PORTB &= ~0x80;
        next_state = &&idle;
    }
    return;
}

И результат:
Код
    .text
    .size    foo, .-foo
.global    __vector_1
__vector_1:
    push __zero_reg__
    push __tmp_reg__
    in __tmp_reg__,__SREG__
    push __tmp_reg__
    clr __zero_reg__
    push r24
    push r25
    push r30
    push r31

    lds r30,next_state.1519
    lds r31,(next_state.1519)+1
    ijmp
.L6:
.L13:
    ldi r24,lo8(gs(.L7))
    ldi r25,hi8(gs(.L7))
    sts (next_state.1519)+1,r25
    sts next_state.1519,r24
    rjmp .L12
.L7:
    sbis 35-0x20,0
    rjmp .L12
    sbi 37-0x20,7
    ldi r24,lo8(gs(.L10))
    ldi r25,hi8(gs(.L10))
    sts (next_state.1519)+1,r25
    sts next_state.1519,r24
    rjmp .L12
.L10:
    sbis 35-0x20,1
    rjmp .L12
    cbi 37-0x20,7
    rjmp .L13
.L12:

    pop r31
    pop r30
    pop r25
    pop r24
    pop __tmp_reg__
    out __SREG__,__tmp_reg__
    pop __tmp_reg__
    pop __zero_reg__
    reti

    .data
next_state.1519:
    .word    gs(.L6)


--------------------
Ну, я пошёл… Если что – звоните…
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Dec 20 2008, 16:26
Сообщение #30


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Цитата(ReAl @ Dec 20 2008, 20:00) *
посмотреть реализацию структуры jump_buf и сделать переключатель

Да-а... На разных платформах количество переключаемого контекста настолько разное... У AVR- многовато sad.gif

Цитата
#define WherePC(a) { a = &&WPC_LABEL(WPC,__LINE__); WPC_LABEL(WPC,__LINE__):; }
................................
static void *next_state = &&start;
goto *next_state;

Спасибо!
Т.е. задача-то решается по-прежнему макросами(для совместимости), но адрес метки в gcc имхо самое не-кривое решение. beer.gif
Go to the top of the page
 
+Quote Post

3 страниц V  < 1 2 3 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 18th July 2025 - 18:00
Рейтинг@Mail.ru


Страница сгенерированна за 0.01572 секунд с 7
ELECTRONIX ©2004-2016