Такое прерывание можно еще оптимизировать?

78125
Offline
Зарегистрирован: 23.09.2015

Добрый день.

Есть желание сделать прерывание по возможности оптимальнее по времени выполнения.

Оно крутит шаговый двигатель и считает шаги, которые использует осн тело программы.

#define MainStStep 8

ISR(TIMER1_B) {
  s1 = not(s1);
  bitWrite(PORTB, MainStStep-8, s1);
  Steps++;
}

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

78125, дизассемблировать надо, и посмотреть во что выливается этот  конкретный фрагмент в листинге , может что немного можно ужать. А радикально оптимизировать -только через ISR_NAKED и на ассемблере вручную  пуш-попить только необходимые регистры в стек. Но обычно кто такое сам может сделать, тот и не спрашивает :)

Upper
Offline
Зарегистрирован: 23.06.2020

По моему сначала надо определить (рассчитать) допустимое время выполнения прерывания. И если укладывается, то код уже оптимальный по некоторым критериям.

Можно вообще обойтись без прерываний, выход менять аппаратно по PWM, а количество - аппаратно вторым таймером, подав ему на вход эти импульсы. Но будет ли это оптимально?

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018
  s1 = not(s1);
  bitWrite(PORTB, MainStStep-8, s1);
PINB=1<<(MainStStep-8)

"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 какой тип имеет ?

dimax пишет:

... дизассемблировать надо ...

Откомпилировать с формированием листинга и глянуть листинг...


 6fa:	1f 92       	push	r1
 6fc:	0f 92       	push	r0
 6fe:	0f b6       	in	r0, 0x3f	; 63
 700:	0f 92       	push	r0
 702:	11 24       	eor	r1, r1
 704:	8f 93       	push	r24
 706:	9f 93       	push	r25
 708:	af 93       	push	r26
 70a:	bf 93       	push	r27
  s1 = not(s1);
 70c:	80 91 3d 01 	lds	r24, 0x013D	; 0x80013d <s1>
 710:	91 e0       	ldi	r25, 0x01	; 1
 712:	89 27       	eor	r24, r25
 714:	80 93 3d 01 	sts	0x013D, r24	; 0x80013d <s1>
  bitWrite(PORTB, MainStStep-8, s1);
 718:	88 23       	and	r24, r24
 71a:	e9 f0       	breq	.+58     	; 0x756 <__vector_13+0x5c>
 71c:	28 9a       	sbi	0x05, 0	; 5
  Steps++;
 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>
}
 744:	bf 91       	pop	r27
 746:	af 91       	pop	r26
 748:	9f 91       	pop	r25
 74a:	8f 91       	pop	r24
 74c:	0f 90       	pop	r0
 74e:	0f be       	out	0x3f, r0	; 63
 750:	0f 90       	pop	r0
 752:	1f 90       	pop	r1
 754:	18 95       	reti
 756:	28 98       	cbi	0x05, 0	; 5
 758:	e2 cf       	rjmp	.-60     	; 0x71e <__vector_13+0x24>

Эта жуть для 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...

На выходе вот такой листинг:

 6fa:	0f 93       	push	r16
 6fc:	0f b7       	in	r16, 0x3f	; 63
 6fe:	0f 93       	push	r16
 700:	18 9a       	sbi	0x03, 0	; 3
 702:	00 91 39 01 	lds	r16, 0x0139	; 0x800139 <Steps>
 706:	0f 5f       	subi	r16, 0xFF	; 255
 708:	00 93 39 01 	sts	0x0139, r16	; 0x800139 <Steps>
 70c:	00 91 3a 01 	lds	r16, 0x013A	; 0x80013a <Steps+0x1>
 710:	0f 4f       	sbci	r16, 0xFF	; 255
 712:	00 93 3a 01 	sts	0x013A, r16	; 0x80013a <Steps+0x1>
 716:	00 91 3b 01 	lds	r16, 0x013B	; 0x80013b <Steps+0x2>
 71a:	0f 4f       	sbci	r16, 0xFF	; 255
 71c:	00 93 3b 01 	sts	0x013B, r16	; 0x80013b <Steps+0x2>
 720:	00 91 3c 01 	lds	r16, 0x013C	; 0x80013c <Steps+0x3>
 724:	0f 4f       	sbci	r16, 0xFF	; 255
 726:	00 93 3c 01 	sts	0x013C, r16	; 0x80013c <Steps+0x3>
 72a:	0f 91       	pop	r16
 72c:	0f bf       	out	0x3f, r16	; 63
 72e:	0f 91       	pop	r16
 730:	18 95       	reti

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Komandir пишет:
Дарю код для long Seps...

Ты сэкономил 3 пуша и 3 попа, то есть 12 тактов. Я имею ввиду не на всем прерывании, а на работе с лонг переменной. 750 наносек. Все норм и красиво, но херней этой заниматься имеет смысл тогда, когда тебе есть кому продать сэкономленные такты. Не в шутку, а на самом деле - операция критична по времени и вот этой самой микросекунды как раз не хватало!

Лезть в ассемблер без нужды? Мне кажется, что даже в хобби результат важнее процесса, но кому - как. ;)) В теории могу представить человека, который покрывает ломберный столик 12-ым слоем лака и вручную шлифует его какой-то волшебной шкуркой из крови единорога и перхоти девственницы! ;))) Вот так и с программированием и ассемблером.

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

wdrakula пишет:

... 3 пуша и 3 попа, ...

Как то странно вы посчитали ...

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Komandir пишет:

wdrakula пишет:

... 3 пуша и 3 попа, ...

Как то странно вы посчитали ...

 

wdrakula пишет:

 Я имею ввиду не на всем прерывании, а на работе с лонг переменной.

Что тебе не ясно? Оба эти кода - 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>

 

78125
Offline
Зарегистрирован: 23.09.2015

Ух спасибо.)

Пожалуй ассембер это уже перебор.

Я правильно понял, что могу эти 2 строки заменить одной: переключения пина на инверсное значение?

s1 = not(s1);
bitWrite(PORTB, MainStStep-8, s1);

PINB=1<<(MainStStep-8);

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

78125 пишет:

Ух спасибо.)

Пожалуй ассембер это уже перебор.

Я правильно понял, что могу эти 2 строки заменить одной: переключения пина на инверсное значение?

s1 = not(s1);
bitWrite(PORTB, MainStStep-8, s1);

PINB=1<<(MainStStep-8);

 

 да!

Странные люди! Ну проверь... хоть в эмуляторе, типа wokwi.com. Неужели проще вопрос задать, чем проверить самому?

78125
Offline
Зарегистрирован: 23.09.2015

уже! спасибо.

а можно хоть какой то комментарий как это работает? Битовый сдвиг из единицы?

Нашел такое https://github.com/AlexGyver/GyverCore/issues/15

"запись в PINx (на ATmega48/88/168/328) инвертирует состояние на выходе.

bitToggle(PORTx, pin);

меняем на...
 

bitSet(PINx, pin);

"

В bitSet как то проще))

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Какая разница ассемблер или нет, когда тебе дали готовый код ? Это же INLINE ASM - вставляется и компилируется прямо из C/C++

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

Да легко!


ISR(TIMER1_B) {
 flag=1;
}

А остальное делать в программе.

SLKH
Offline
Зарегистрирован: 17.08.2015

78125 пишет:

уже! спасибо.

а можно хоть какой то комментарий как это работает? Битовый сдвиг из единицы?

Нашел такое https://github.com/AlexGyver/GyverCore/issues/15

"запись в PINx (на ATmega48/88/168/328) инвертирует состояние на выходе.

bitToggle(PORTx, pin);

меняем на...
 

bitSet(PINx, pin);

"

В bitSet как то проще))

А ты это проверял?

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018
Описано так:
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
 
bitSet(PINB,MainStStep-8);
порождает код:
 6fa: 18 9a        sbi 0x03, 0 ; 3
 

 

78125
Offline
Зарегистрирован: 23.09.2015

mykaida пишет:

Да легко!

А остальное делать в программе.

 

Не пойдет, прерывание вызывается достаточно часто, до обработкив теле программы может не успеть дойти, пока вызовется второе прерывание, которое снова поставит flag и при подсчете шагов, один шаг уже будет потерян.

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

78125 Steps какого типа у вас ? 

78125
Offline
Зарегистрирован: 23.09.2015
volatile unsigned long Steps = 0;

 

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Тогда ничего менять в том моём коде на ASM не надо. В AVR нет команды на прибавление непосредственного значения - по этому используется вычитание: Steps+1 = Steps-(-1). -1 для long это 0xFFFFFFFF - вот его и вычитаем из всех байтов по очереди с учетом возможного переноса/заёма ...

78125
Offline
Зарегистрирован: 23.09.2015

Спасибо!

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Можно конечно вставить проверку на перенос/заём после каждого вычитания, но тогда время нахождения в прерывании будет отличаться в зависимости от того сколько раз произошёл перенос/заём...

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))
    :	);
}

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

А вообще, оно надо 4-байтовый Steps в прерывании теребить? Может выделить под это один счётный регистр Rx, его в прерывании инкрементировать, а в лупе уже  приплюсовывать его к Steps. Тогда прерывание ещё в несколько раз ужмётся)

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

dimax пишет:

А вообще, оно надо 4-байтовый Steps в прерывании теребить? Может выделить под это один счётный регистр Rx, его в прерывании инкрементировать, а в лупе уже  приплюсовывать его к Steps. Тогда прерывание ещё в несколько раз ужмётся)

Если две-три мкс имеют значение, видимо, нужно перепроектировать изделие? Не те времена, чтобы вытягивать из "дорогущего" микроконтроллера предел его возможностей. ;)