Такое прерывание можно еще оптимизировать?
- Войдите на сайт для отправки комментариев
Ср, 06/07/2022 - 00:01
Добрый день.
Есть желание сделать прерывание по возможности оптимальнее по времени выполнения.
Оно крутит шаговый двигатель и считает шаги, которые использует осн тело программы.
#define MainStStep 8
ISR(TIMER1_B) {
s1 = not(s1);
bitWrite(PORTB, MainStStep-8, s1);
Steps++;
}
78125, дизассемблировать надо, и посмотреть во что выливается этот конкретный фрагмент в листинге , может что немного можно ужать. А радикально оптимизировать -только через ISR_NAKED и на ассемблере вручную пуш-попить только необходимые регистры в стек. Но обычно кто такое сам может сделать, тот и не спрашивает :)
По моему сначала надо определить (рассчитать) допустимое время выполнения прерывания. И если укладывается, то код уже оптимальный по некоторым критериям.
Можно вообще обойтись без прерываний, выход менять аппаратно по PWM, а количество - аппаратно вторым таймером, подав ему на вход эти импульсы. Но будет ли это оптимально?
"Toggling the Pin.
Writing a logic one to PINxn toggles the value of PORTxn, independent on the value of DDRxn. Note that the SBI instruction can be used to toggle one single bit in a port."
Плюс ISR_NAKED как dimax уже описал.
Steps какой тип имеет ?
... дизассемблировать надо ...
Откомпилировать с формированием листинга и глянуть листинг...
Эта жуть для long Steps...
Дарю код для long Seps...
ISR(TIMER1_B, ISR_NAKED) { asm volatile( "PUSH R16 \n\t" "IN R16, %[sreg] \n\t" "PUSH R16 \n\t" "SBI %[pinb],%[mss] \n\t" "LDS R16,%[steps] \n\t" "SUBI R16,0xFF \n\t" "STS %[steps],R16 \n\t" "LDS R16,%[steps]+1 \n\t" "SBCI R16,0xFF \n\t" "STS %[steps]+1,R16 \n\t" "LDS R16,%[steps]+2 \n\t" "SBCI R16,0xFF \n\t" "STS %[steps]+2,R16 \n\t" "LDS R16,%[steps]+3 \n\t" "SBCI R16,0xFF \n\t" "STS %[steps]+3,R16 \n\t" "POP R16 \n\t" "OUT %[sreg],R16 \n\t" "POP R16 \n\t" "RETI \n\t" : : [sreg] "I" (_SFR_IO_ADDR(SREG)), [pinb] "I" (_SFR_IO_ADDR(PINB)), [mss] "I" (MainStStep-8), [steps] "i" (_SFR_MEM_ADDR(Steps)) : ); }Строки с 10 по 18 надо удалять в зависимости от типа Steps...
На выходе вот такой листинг:
Ты сэкономил 3 пуша и 3 попа, то есть 12 тактов. Я имею ввиду не на всем прерывании, а на работе с лонг переменной. 750 наносек. Все норм и красиво, но херней этой заниматься имеет смысл тогда, когда тебе есть кому продать сэкономленные такты. Не в шутку, а на самом деле - операция критична по времени и вот этой самой микросекунды как раз не хватало!
Лезть в ассемблер без нужды? Мне кажется, что даже в хобби результат важнее процесса, но кому - как. ;)) В теории могу представить человека, который покрывает ломберный столик 12-ым слоем лака и вручную шлифует его какой-то волшебной шкуркой из крови единорога и перхоти девственницы! ;))) Вот так и с программированием и ассемблером.
... 3 пуша и 3 попа, ...
Как то странно вы посчитали ...
... 3 пуша и 3 попа, ...
Как то странно вы посчитали ...
Я имею ввиду не на всем прерывании, а на работе с лонг переменной.
Что тебе не ясно? Оба эти кода - 20 тактов. Разница в регистрах. Ты использовал 1, а компилятор черыре, на 6 команд == на 12 тактов больше при входе/выходе из прерывания. Что-то еще посчитать за тебя?
"LDS R16,%[steps] \n\t" "SUBI R16,0xFF \n\t" "STS %[steps],R16 \n\t" "LDS R16,%[steps]+1 \n\t" "SBCI R16,0xFF \n\t" "STS %[steps]+1,R16 \n\t" "LDS R16,%[steps]+2 \n\t" "SBCI R16,0xFF \n\t" "STS %[steps]+2,R16 \n\t" "LDS R16,%[steps]+3 \n\t" "SBCI R16,0xFF \n\t" "STS %[steps]+3,R16 \n\t" 71e: 80 91 39 01 lds r24, 0x0139 ; 0x800139 <Steps> 722: 90 91 3a 01 lds r25, 0x013A ; 0x80013a <Steps+0x1> 726: a0 91 3b 01 lds r26, 0x013B ; 0x80013b <Steps+0x2> 72a: b0 91 3c 01 lds r27, 0x013C ; 0x80013c <Steps+0x3> 72e: 01 96 adiw r24, 0x01 ; 1 730: a1 1d adc r26, r1 732: b1 1d adc r27, r1 734: 80 93 39 01 sts 0x0139, r24 ; 0x800139 <Steps> 738: 90 93 3a 01 sts 0x013A, r25 ; 0x80013a <Steps+0x1> 73c: a0 93 3b 01 sts 0x013B, r26 ; 0x80013b <Steps+0x2> 740: b0 93 3c 01 sts 0x013C, r27 ; 0x80013c <Steps+0x3>Ух спасибо.)
Пожалуй ассембер это уже перебор.
Я правильно понял, что могу эти 2 строки заменить одной: переключения пина на инверсное значение?
Ух спасибо.)
Пожалуй ассембер это уже перебор.
Я правильно понял, что могу эти 2 строки заменить одной: переключения пина на инверсное значение?
да!
Странные люди! Ну проверь... хоть в эмуляторе, типа wokwi.com. Неужели проще вопрос задать, чем проверить самому?
уже! спасибо.
а можно хоть какой то комментарий как это работает? Битовый сдвиг из единицы?
Нашел такое https://github.com/AlexGyver/GyverCore/issues/15
"запись в PINx (на ATmega48/88/168/328) инвертирует состояние на выходе.
меняем на...
"ВbitSet как то проще))Какая разница ассемблер или нет, когда тебе дали готовый код ? Это же INLINE ASM - вставляется и компилируется прямо из C/C++
Да легко!
ISR(TIMER1_B) { flag=1; }А остальное делать в программе.
уже! спасибо.
а можно хоть какой то комментарий как это работает? Битовый сдвиг из единицы?
Нашел такое https://github.com/AlexGyver/GyverCore/issues/15
"запись в PINx (на ATmega48/88/168/328) инвертирует состояние на выходе.
меняем на...
"ВbitSet как то проще))Да легко!
А остальное делать в программе.Не пойдет, прерывание вызывается достаточно часто, до обработкив теле программы может не успеть дойти, пока вызовется второе прерывание, которое снова поставит flag и при подсчете шагов, один шаг уже будет потерян.
78125 Steps какого типа у вас ?
Тогда ничего менять в том моём коде на ASM не надо. В AVR нет команды на прибавление непосредственного значения - по этому используется вычитание: Steps+1 = Steps-(-1). -1 для long это 0xFFFFFFFF - вот его и вычитаем из всех байтов по очереди с учетом возможного переноса/заёма ...
Спасибо!
Можно конечно вставить проверку на перенос/заём после каждого вычитания, но тогда время нахождения в прерывании будет отличаться в зависимости от того сколько раз произошёл перенос/заём...
ISR(TIMER1_B, ISR_NAKED) { asm volatile( "PUSH R16 \n\t" "IN R16, %[sreg] \n\t" "PUSH R16 \n\t" "SBI %[pinb],%[mss] \n\t" "LDS R16,%[steps] \n\t" "SUBI R16,0xFF \n\t" "STS %[steps],R16 \n\t" "BRCC exit \n\t" "LDS R16,%[steps]+1 \n\t" "SBCI R16,0xFF \n\t" "STS %[steps]+1,R16 \n\t" "BRCC exit \n\t" "LDS R16,%[steps]+2 \n\t" "SBCI R16,0xFF \n\t" "STS %[steps]+2,R16 \n\t" "BRCC exit \n\t" "LDS R16,%[steps]+3 \n\t" "SBCI R16,0xFF \n\t" "STS %[steps]+3,R16 \n\t" "exit: \n\t" "POP R16 \n\t" "OUT %[sreg],R16 \n\t" "POP R16 \n\t" "RETI \n\t" : : [sreg] "I" (_SFR_IO_ADDR(SREG)), [pinb] "I" (_SFR_IO_ADDR(PINB)), [mss] "I" (MainStStep-8), [steps] "i" (_SFR_MEM_ADDR(Steps)) : ); }А вообще, оно надо 4-байтовый Steps в прерывании теребить? Может выделить под это один счётный регистр Rx, его в прерывании инкрементировать, а в лупе уже приплюсовывать его к Steps. Тогда прерывание ещё в несколько раз ужмётся)
А вообще, оно надо 4-байтовый Steps в прерывании теребить? Может выделить под это один счётный регистр Rx, его в прерывании инкрементировать, а в лупе уже приплюсовывать его к Steps. Тогда прерывание ещё в несколько раз ужмётся)
Если две-три мкс имеют значение, видимо, нужно перепроектировать изделие? Не те времена, чтобы вытягивать из "дорогущего" микроконтроллера предел его возможностей. ;)