некоторые вопросы по прерываниям и стеку UART для меги.

findeler
Offline
Зарегистрирован: 08.03.2016

1. Правильно ли я понял что прерывания меги это 2,3,21,20,19,18 пины. И слушать и читать  (через прерывания)    я могу только  первый hardware serial (RX1-19pin) ?

2. Какой аппаратный стек по приёму данных через  UART  у arduino mega ?

findeler
Offline
Зарегистрирован: 08.03.2016

собственно по прерываниям непонятно. В даташите на 2560

RXD3/PCINT9

RXD1/PCINT2

RXD0/PCINT8

И только RXD2 не имеет прерывания.

В описании на сайте 2,3,21,20,19,18 хотя в даташите на 24 пина могут быть привязаны вектора прерываний. Где ошибка.

findeler
Offline
Зарегистрирован: 08.03.2016

А если читать ещё дальше. То все Uart имеют свои вектора прерываний. В описании на сайте ошибка.

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

findeler, не понятно почему вы видите какую-то связь между uart и прерываниями, это же совсем независимые вещи. Так же прерывания деляться на групповые PCINT0-PCINT23, которые группируются в три вектора,  и 8  выделенных INT0-INT7, у них индивидуальные векторы. Про групповые вроде на сайте не упоминается, а про индвидуальные сказано. Только распаяно на плате  их 6, поэтому и не пишут что их 8.

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

Я попробую объяснить.

Видимо есть непонимание природы такго явления, как прерывания.

Прерывания не обязательно связаны с ножкой контроллера.

Прерывание, вообще, это вызов некой функции в момент наступления события.

Бесспорно, что событие может быть внешним - изменение на ноже контроллера, но есть и ВНУТРЕННИЕ события, как то: события таймера (переполнение, достижение некоего значения), события АЦП - завершение преобразования, события УАРТ - завершение передачи или приема.

-------------------

Вы просто немного запутались, смешав ножки, на которых могут "висеть" те или иные функции и события.

 

findeler
Offline
Зарегистрирован: 08.03.2016

Ну в описании зачем то выбранны ножки которые пересекаются с выходами под UART. Тогда как в даташите INT0-7 направленны на другие ноги.

Разницу понимаю, хоть не совсем понимаю механизмы. Но в целом понятно.

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

функций у контроллера значительно больше, чем ног. Каждая ножка может выполнять много разных функций. Так что на одной ноге может быть и Rx UART и прерывание и еще что-то. Но в программе - это совершенно разные веши.

 

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

findeler пишет:

RXD3/PCINT9

...

эта запись означает, что на ноге, которая МОЖЕТ выполнять функцию RX  третьего УАРТ, также подключена функция PCINT9.

То есть ЕСЛИ в программе не используется УАРТ 3, и порт PJ0  настроен на вход, то можно использовать пин-чейндж-интерапт номер 9. Так стало понятнее?

findeler
Offline
Зарегистрирован: 08.03.2016

wdrakula пишет:

эта запись означает, что на ноге, которая МОЖЕТ выполнять функцию RX  третьего УАРТ, также подключена функция PCINT9.

То есть ЕСЛИ в программе не используется УАРТ 3, и порт PJ0  настроен на вход, то можно использовать пин-чейндж-интерапт номер 9. Так стало понятнее?

Вроде разобрался, меня в общем конкретно интересовал вопрос возможности слушать все UART порты.

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

Я бы ещё так объяснил про прерывания: то, что на ножках (INT, PCINT) - это внешние прерывания (External Interrupt), которые срабатывают при изменении лог. уровня на ножке. Они никак не относятся к переферии и прерываниям переферии (UART, таймеры и т.д.) А прерывания переферии можно назвать "внутренними прерываниями". Хотя переферия тоже может иметь выход наружу (напр. UART), но на этих ножках не обязаны быть внешние прерывания. А на распиновке в даташите, где на ножках указано несколько функций - означает, что на этой ножке можно задействовать одну из указанных функций. Например, если указано RXD3/PCINT9 - то можно использовать либо RX уарта на этой ножке, либо внешнее прерывание PCINT. Одновременно их задействовать нельзя.

ptr
Offline
Зарегистрирован: 28.05.2016

Jeka_M пишет:

Я бы ещё так объяснил про прерывания: то, что на ножках (INT, PCINT) - это внешние прерывания (External Interrupt), которые срабатывают при изменении лог. уровня на ножке.

И это не совсем так. Цитирую даташит:
"The interrupts will trigger even if the INT0 and INT1 or PCINT23...0 pins are configured as outputs. This feature provides a way of generating a software interrupt."

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

Спасибо за уточнение. То есть, ножку с внешним прерыванием (INT, PCINT) можно сконфигурировать на выход и программно меняя лог. уровень - вызывать прерывание и выполнять обработчик внешнего прерывания? Не совсем понимаю, где бы это могло пригодиться.

ptr
Offline
Зарегистрирован: 28.05.2016

Jeka_M пишет:

Не совсем понимаю, где бы это могло пригодиться.

Смысл тот же, что и программные прерывания на PC. Они выполняются выстрее, чем вызов подпрограммы.

a5021
Offline
Зарегистрирован: 07.07.2013

Они выполняются медленнее вообще-то. Вызов подпрограммы не требует переключения контекста, а для прерывания это обязательная процедура.

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

Забыл ещё для топикстартёра добавить: в даташите есть таблица векторов прерываний (Table 14-1. Reset and Interrupt Vectors). Там хорошо видно, что External Interrupt (INT), Pin Change Interrupt (PCINT) и различные прерывания переферии (того же UART) - имеют разные векторы.

ptr
Offline
Зарегистрирован: 28.05.2016

a5021 пишет:

Они выполняются медленнее вообще-то. Вызов подпрограммы не требует переключения контекста, а для прерывания это обязательная процедура.

AVR не сохраняет контекст при прерываниях. То есть, сохранность регистра флагов лежит на обработчике прерывания.

Вызов прерывания выполнятся за 4 такта. А команда CALL есть только у ATmega168PA и ATmega328P. Остальным AVR доступны только команды RCALL и ICALL которые не позволяют адресовать все адресное пространство. А значит, потребуют еще дополнительных RJMP. То есть получится не менее 5 циклов при использовании RCALL + RJMP.

Если, конечно у нас вызов подпрограммы в пределах +-4К от точки вызова, только тогда RCALL окажется быстрее, чем прерывание.

a5021
Offline
Зарегистрирован: 07.07.2013

ptr пишет:
AVR не сохраняет контекст при прерываниях. То есть, сохранность регистра флагов лежит на обработчике прерывания.

Вы исходите из предположения, что в момент возникновения прерывания ни один из 32-х регистров общего назначения не использовался программой и сохранять их не нужно? Флаги сохранили, а там трава не расти? Как бы не так.

Обязанность сохранения контекста лежит на компиляторе, а не на AVR. Процессор не может знать заранее, какие регистры используются обработчиком, а сохранять каждый раз все накладно в терминах прозводительности. Поэтому все отдано на откуп компилятору, который точно знает, что и когда сохранять.

В качестве примера можно рассмотреть обработчик прерывания таймера 0, который присутствует в любом скетче, если последний составлен без переопределения процедуры стандартной ардуиновской инициализации.

        ;; ISR(TIMER0_OVF_vect) {
        
        ;; обработчик начинается с 
        ;; кода сохранения контекста

 2da:	1f 92       	push	r1
 2dc:	0f 92       	push	r0
 2de:	0f b6       	in	r0, 0x3f	; 63
 2e0:	0f 92       	push	r0
 2e2:	11 24       	eor	r1, r1
 2e4:	2f 93       	push	r18
 2e6:	3f 93       	push	r19
 2e8:	8f 93       	push	r24
 2ea:	9f 93       	push	r25
 2ec:	af 93       	push	r26
 2ee:	bf 93       	push	r27

        ;; Тут идет код самого обработчика,
        ;; который здесь не приводится

        ;; в конце обработчика идет
        ;; код восстановления контекста:

 358:	bf 91       	pop	r27
 35a:	af 91       	pop	r26
 35c:	9f 91       	pop	r25
 35e:	8f 91       	pop	r24
 360:	3f 91       	pop	r19
 362:	2f 91       	pop	r18
 364:	0f 90       	pop	r0
 366:	0f be       	out	0x3f, r0	; 63
 368:	0f 90       	pop	r0
 36a:	1f 90       	pop	r1
 36c:	18 95       	reti

Итого, 22 команды, а вовсе не четыре такта. В общем случае прерывание не только не быстрее функции, а медленне и медленнее намного.

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

Ptr! Вы и в самом деле немного ошиблись. Обработчик прерываний вызывать всегда дороже, чем просто процедуру. По совокупности затрат. 

Пути, которыми следует ошибившийся, много говорят о человеке. Есть такие как некто с буддистским именем ;). Никогда не признает ошибки. Поэтому и глумится над ним Евгений, что будучи прижат тем, что вычисления в стандарте С производятся после приведения к int, стал плеваться и говорить про косяк компилятора/стандарта и всех нас, "рабов копроэкономики" (с). ;) ;) ;).

Тут, про необязательность настройки  пина на вход, Вы абсолютно правы, мой ошибка, забыл.

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

---------------------

Да, и про ядро Линукса. Так уж получилось, что лет 10-12 назад я переписывал большие куски ядра (2.2, старого) адаптируя под ARM 7. Типа Ай-пи телефона делал. Правил кусками жуткий индийский (в самом деле индийский - там коментарии на ломаном аглицком были) код.

Вы не поверите, сколько частей кода написано на ООП! И классы применены весьма кстати, чаще всего.

ptr
Offline
Зарегистрирован: 28.05.2016

wdrakula пишет:

Да, и про ядро Линукса. Так уж получилось, что лет 10-12 назад я переписывал большие куски ядра (2.2, старого) адаптируя под ARM 7.

Вы не поверите, сколько частей кода написано на ООП! И классы применены весьма кстати, чаще всего.

Не верю. Правда самое старое ядро, которое я не убил на своей Gentoo, 3.5.7, но ни в нем, ни в последующих, ни одного *.cpp файла я не нашел. Можно конкретней?

 

ptr
Offline
Зарегистрирован: 28.05.2016

a5021 пишет:

ptr пишет:
AVR не сохраняет контекст при прерываниях. То есть, сохранность регистра флагов лежит на обработчике прерывания.

Вы исходите из предположения, что в момент возникновения прерывания ни один из 32-х регистров общего назначения не использовался программой и сохранять их не нужно?

Нет. Я исхожу из предположения, что хоть прерывание, хоть функция, без разницы, должна сохранить содержимое тех регистров, которое она модифицирует.

Цитата:

Обязанность сохранения контекста лежит на компиляторе, а не на AVR.

А при чем тут компилятор? Я никак не уточнял, идет тут речь про ассемблер или какой-либо компилятор.

 

Цитата:

Итого, 22 команды, а вовсе не четыре такта. В общем случае прерывание не только не быстрее функции, а медленне и медленнее намного.

Самое интересное удалили. А именно, почему компилятор сохранял в стек именно эти регистры. Еще раз, без разницы, какая функция, обычная или обработчик прерываний, она в любом случае будет сохранять содержимое регистров, которые она использует. Есть еще варианты для функций явно описанных, как static, то есть когда компилятор знает, что вызываться они могут только из компилируемого файла и он знает откуда. Но если это обычная функция, связываемая линкером, то она понятия не имеет, в каких регистрах что есть и потому будет сохранять все, что использует.

a5021
Offline
Зарегистрирован: 07.07.2013

ptr пишет:
Я исхожу из предположения, что хоть прерывание, хоть функция, без разницы, должна сохранить содержимое тех регистров, которое она модифицирует.

Заблуждение. В случае с функцией, компилятор знает, какие регистры использовались до ее вызова. Если алгоритму в функции хватит, то компилятор использует свободные и не будет сохранять вообще ничего. Если посмотреть дизасемблированные листинги ардуиновских скетчей, то большинство вызовов так и делается. Когда места под локальные переменные не хватает, компилятор отправляет в стек пару-тройку регистров. Так происходит потому, что у компилятора есть возможности для маневра и оптимизации, как, где и какие регистры использовать. Код выполняется последовательно, включая вызов функции, и компилятор прекрасно видит всю картину целиком и не испытывает трудностей с оптимальным распределением ресурсов.

Прервывание, в отличие от функции, может случиться в любой момент. Какие ресурсы ЦП использовались до прерывания, ни компилятор, ни программист на ассемблере, заранее предсказать не состоянии. Поэтому сохраняются все регистры, значения которых меняется кодом обработчика. Это процедура выполняется независимо от того, было ли чего ценного в этих регистрах или они содержали мусор.

Цитата:
А при чем тут компилятор? Я никак не уточнял, идет тут речь про ассемблер или какой-либо компилятор.

Действия компилятора и программиста на ассемблере здесь могут как-то отличаться? Что за безсмысленное возражение?

Цитата:
Самое интересное удалили. А именно, почему компилятор сохранял в стек именно эти регистры.

Потому, что ответ лежит на поверхности: какие регистры изменяются в обработчике, те и сохраняются. Ничего тут "интересного" нет. Все банально.

Цитата:
Но если это обычная функция, связываемая линкером, то она понятия не имеет, в каких регистрах что есть и потому будет сохранять все, что использует.

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

Вот вам loop() с самого начала, где внутри лупа в цикле вызываются analogWrite() и delay():

        ;; void loop() {
 28e:	c0 e0       	ldi	r28, 0x00	; 0
 290:	d0 e0       	ldi	r29, 0x00	; 0
 292:	be 01       	movw	r22, r28
 294:	81 2f       	mov	r24, r17
 296:	27 d1       	rcall	.+590    	; 0x4e6 <analogWrite>
 298:	62 e0       	ldi	r22, 0x02	; 2
        ;; delay(2);
 29a:	70 e0       	ldi	r23, 0x00	; 0
 29c:	80 e0       	ldi	r24, 0x00	; 0
 29e:	90 e0       	ldi	r25, 0x00	; 0
 2a0:	89 d0       	rcall	.+274    	; 0x3b4 <delay>
 2a2:	21 96       	adiw	r28, 0x01	; 1
 2a4:	cf 3f       	cpi	r28, 0xFF	; 255

Функция analogWrite() сохраняет на входе три регистра, а использует чуть ни все из имеющихся, плюс хрен знает куда еще ходит и что делает по вложенным вызовам.

           ;; void analogWrite(uint8_t pin, int val) {
 4e6:	1f 93       	push	r17
 4e8:	cf 93       	push	r28
 4ea:	df 93       	push	r29
 4ec:	18 2f       	mov	r17, r24
 4ee:	eb 01       	movw	r28, r22
	    ;; pinMode(pin, OUTPUT);
 4f0:	61 e0       	ldi	r22, 0x01	; 1
 4f2:	0f d1       	rcall	.+542    	; 0x712 <pinMode>
 4f4:	20 97       	sbiw	r28, 0x00	; 0
...

Дальше сами смотрите. Может научитесь чему-нибудь.

ptr
Offline
Зарегистрирован: 28.05.2016

a5021 пишет:

В случае с функцией, компилятор знает, какие регистры использовались до ее вызова.

Утверждение ложное. Компилятор может знать о том, какие регистры использовались до вызова функции только при соблюдении следующих условий:
1. Функция статическая, не может быть вызвана из другого объектного модуля и не может быть включена в библиотеку.
2. Нигде в пределах исходного файла никакому указателю на функцию не присваивался адрес этой функции.

В остальных случаях компилятор ничего не знает о том, откуда функция может быть вызвана и, следовательно, ничего не знает о том, какие регистры до вызова функции использовались, а какие нет. Он даже не знает, вызвана ли была эта функция из объектного модуля скомпилированного GCC, а не каким-то другим компилятором другого языка или даже из ассемблера.

В принципе, GCC позволяет сообщить компилятору о том, какие регистры и для чего следует использовать: https://gcc.gnu.org/onlinedocs/gccint/Registers.html

Но в Arduino IDE это не используется и пользоваться этим надо очень аккуратно и очень осторожно.

Цитата:

Прервывание, в отличие от функции, может случиться в любой момент.

Точно так же, как и вызов функции. Например, ни один из известных мне компиляторов не способен отследить, какое значение будет у указателя на функцию, в момент вызова функции по этому указателю.

Цитата:

Действия компилятора и программиста на ассемблере здесь могут как-то отличаться? Что за безсмысленное возражение?

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

Цитата:

какие регистры изменяются в обработчике, те и сохраняются

Наконец то! И тоже самое происходит и в функции. Один к одному!

Цитата:
Но если это обычная функция, связываемая линкером, то она понятия не имеет, в каких регистрах что есть и потому будет сохранять все, что использует.

Цитата:

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

Вздор несете как раз Вы, так как по коду видно, что вызов функции обеспечен компилятором, а не отдан на откуп ld. Был бы ld, вместо явного оффсета была бы внешняя метка, которую уже обрабатывал бы линкер. Я Вам про общий случай в крупных многофайловых проектах, а Вы мне частный случай с однофайловым примером в ответ.

Цитата:

Дальше сами смотрите. Может научитесь чему-нибудь.

Вот именно )

a5021
Offline
Зарегистрирован: 07.07.2013

ptr пишет:

Компилятор может знать о том, какие регистры использовались до вызова функции только при соблюдении следующих условий:

1. Функция статическая, не может быть вызвана из другого объектного модуля и не может быть включена в библиотеку.
2. Нигде в пределах исходного файла никакому указателю на функцию не присваивался адрес этой функции.

Полная чушь. Сейчас специально добавил в ранее цитируемый пример указатель на analogWrite(), которая оказалась еще и ни разу не static, да и стал вызывать ее по указателю:

void (*analogWriteP)(uint8_t, int);

void setup() {
...
  analogWriteP = analogWrite;
}

void loop() {
    ...
    analogWriteP(thisPin, brightness);
    delay(2);
...
}

Компилятор тупо сменил тип вызова с относительного на косвенный (indirect), на чем и ограничился:

            ;; void loop() {
 120:    c0 e0           ldi    r28, 0x00    ; 0
 122:    d0 e0           ldi    r29, 0x00    ; 0
 124:    e0 91 00 01     lds    r30, 0x0100
 128:    f0 91 01 01     lds    r31, 0x0101
 12c:    be 01           movw    r22, r28
 12e:    81 2f           mov    r24, r17
 130:    09 95           icall

Код самой функции остался без изменений настолько, что приводить его тут еще раз не имеет никакого смысла. Ничего не хотите сказать по поводу своей сверх-развитой фантазии?

Цитата:
В остальных случаях компилятор ничего не знает о том, откуда функция может быть вызвана и, следовательно, ничего не знает о том, какие регистры до вызова функции использовались, а какие нет.

Ахинея. Как вы себе представляете вызов функции неизвестно откуда? Вызов функции в программе атмеги может состоятся лишь в случае, когда по ходу выполнения, процессор встречает команды Call, iCall или rCall. Никаким другим способом функция самовызваться неизвестно откуда не может. Последовательность команд в двоичном коде составляет компилятор. Как он может не знать, где он поставил команду вызова функции? Какую ересь вы тут несете?

Цитата:
Он даже не знает, вызвана ли была эта функция из объектного модуля скомпилированного GCC, а не каким-то другим компилятором другого языка или даже из ассемблера.

Зачем вы всю эту хрень сюда прете? Я даже вникать не буду, что вы тут набарабанили. Разговор был о том, что быстрее, обычная функция или прерывание. Нахрена они мне сдались, эти "копиляторы другого языка" ? Их кто-то использует? Вы можете представить массу примеров такого рода для ардуины? Или это банальный демагогический прием для увода темы? Ась?

Цитата:
Точно так же, как и вызов функции. Например, ни один из известных мне компиляторов не способен отследить, какое значение будет у указателя на функцию, в момент вызова функции по этому указателю.

Да компилятору, как бы и все равно, куда там он будет указывать. Если пользователь балбес и вызывает функцию по дикому указателю, то это его проблемы. С другой стороны, если указатель объявлен с контролем по прототипу (как в моем примере) и в тексте присутствует в явном виде присвоение ему адреса конкретной функции (опять же, как у меня), то компилятору совсем не обязательно располагать сврехпроницательностью, чтобы учесть в коде всех функций, если вдруг их окажется даже несколько и которые могут быть вызваны таким образом, все необходимое оптимальным образом.

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

Я вам хочу острожно напомнить, что мы на форуме ардуины и тут вряд ли найдется много участников, кого впечатлит долгая и героическая борьба с ассемблером. Вы конечно поспрошайте, вдруг я не прав и к вам в ученики народ уже выстраивается в очередь, только до ваших откровений никто почему-то особого интереса к ассемблеру здесь не проявлял.

Цитата:
И тоже самое происходит и в функции. Один к одному!

Я вам привел листинг и функции и обработчика. Они оказались далеко не "один к одному". И после этого вы не нашли ничего лучше, чем пересочинить свою догму еще раз? Пытаетесь взять не аргументами, но настойчивостью?

Цитата:
Я Вам про общий случай в крупных многофайловых проектах, а Вы мне частный случай с однофайловым примером в ответ.

А какова доказательная ценность этих ваших неназванных "крупных многофайловых проектов", если вы даже не пояснили, о чем речь? Где можно посмотреть на такие "проекты", держа в уме, что ардуино -- это в 90% случаев простые устройства и простые скетчи, а мой "частный случай" накрывает, если не большинство подобных применений, то весьма значительную их часть?

Что такое "многофайловый" ? 20 отдельных файлов, из которых собирается результирующий код -- это уже "многофйаловый" или еще "малофайловый" ? Создается впечатление, что конкретика явно не ваш конёк.

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

ptr
Offline
Зарегистрирован: 28.05.2016

a5021 пишет:

Полная чушь.

Ахинея.

a5021, повторим: http://arduino.ru/forum/apparatnye-voprosy/vybor-tranzistorov-dlya-kommu...

А чтобы не позориться, почитайте, хотя бы вики: https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BC%D0%BF%D0%BE%D0%BD%D0%BE...

ptr
Offline
Зарегистрирован: 28.05.2016

a5021 пишет:

Вот вам loop() с самого начала, где внутри лупа в цикле вызываются analogWrite() и delay():

        ;; void loop() {
 28e:	c0 e0       	ldi	r28, 0x00	; 0
 290:	d0 e0       	ldi	r29, 0x00	; 0
 292:	be 01       	movw	r22, r28
 294:	81 2f       	mov	r24, r17
 296:	27 d1       	rcall	.+590    	; 0x4e6 <analogWrite>
 298:	62 e0       	ldi	r22, 0x02	; 2
        ;; delay(2);
 29a:	70 e0       	ldi	r23, 0x00	; 0
 29c:	80 e0       	ldi	r24, 0x00	; 0
 29e:	90 e0       	ldi	r25, 0x00	; 0
 2a0:	89 d0       	rcall	.+274    	; 0x3b4 <delay>
 2a2:	21 96       	adiw	r28, 0x01	; 1
 2a4:	cf 3f       	cpi	r28, 0xFF	; 255

Функция analogWrite() сохраняет на входе три регистра, а использует чуть ни все из имеющихся, плюс хрен знает куда еще ходит и что делает по вложенным вызовам.

           ;; void analogWrite(uint8_t pin, int val) {
 4e6:	1f 93       	push	r17
 4e8:	cf 93       	push	r28
 4ea:	df 93       	push	r29
 4ec:	18 2f       	mov	r17, r24
 4ee:	eb 01       	movw	r28, r22
	    ;; pinMode(pin, OUTPUT);
 4f0:	61 e0       	ldi	r22, 0x01	; 1
 4f2:	0f d1       	rcall	.+542    	; 0x712 <pinMode>
 4f4:	20 97       	sbiw	r28, 0x00	; 0
...

Дальше сами смотрите. Может научитесь чему-нибудь.

Для тех кто читает поясню.

Функция сохранила все регистры, которые она использует и которые не сохраняет вызывающая программа: 17, 28 и 29. Вызывающая программа передала функции pin в регистре 24, а val в паре регистров 22 и 23. Доступными для использования остались регистры 18-21 и 30-31.

Подробности здесь: http://www.atmel.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_reg_usage.html

Иными словами, обязанность сохранения значения 12 регистов r18-r27, r30-r31 лежит на вызывающей программе. Обязанность сохранения значений 18 регистров r2-r17, r28-r29 лежит на вызываемой программе. Регистры r0 и r1 для переменных GCC не используются.

Так как в вызывающей функции не было ни аргументов, ни переменных, ей ничего сохранять в вышеприведенном примере не пришлось.

a5021
Offline
Зарегистрирован: 07.07.2013

Я вот что-то не понял, прерывание уже стало работать быстрее функции или вы опять произвели на свет впечатляющее количество словесной шелухи ни о чем?

Ну а то, что собственный слив вы никогда не признаете, а предпочтете в щекотливой ситувации по-бабьи закатиться в тошнотворной истерике под надуманным предлогом, я уже давно понял. У вас нет выбора и вам придется заниматься этим постоянно, если не сделаете соответствующих выводов.

ptr
Offline
Зарегистрирован: 28.05.2016

a5021 пишет:

Я вот что-то не понял, прерывание уже стало работать быстрее функции

Не медленней, но с экономией на каждом вызове 2 байт флеша.

a5021
Offline
Зарегистрирован: 07.07.2013

Вы ж говорили быстрее. По прежнему жду доказательств. То, что оно будет медленнее и медленнее намного, я уже объяснил и показал на примерах. Вы не привели в защиту свой точки зрения ничего. Ну а пустые словеса, они пустые словеса и есть. Кто хочет, пусть на них ориентируется, его проблема.

ptr
Offline
Зарегистрирован: 28.05.2016

a5021 пишет:

Вы ж говорили быстрее. По прежнему жду доказательств.

На 8-битных AVR, на которых есть 32-х битная команда команда CALL - столько же времени. На остальных - столько же, если подпрограмма находится в пределах доступности команды RCALL и быстрее, если приходится использовать команду ICALL.

То есть, в общем случае быстрее, в частных случаях - столько же.

Цитата:

То, что оно будет медленнее и медленнее намного, я уже объяснил и показал на примерах.

Ничего не показали. Я доказал, что все регистры при вызове функции все равно сохраняются. Часть вызывающей функцией, часть вызываемой. А других аргументов, кроме времени на сохранение регистров у Вас не было.

 

Logik
Offline
Зарегистрирован: 05.08.2014

ptr пишет:

Функция сохранила все регистры, которые она использует и которые не сохраняет вызывающая программа: 17, 28 и 29. Вызывающая программа передала функции pin в регистре 24, а val в паре регистров 22 и 23. Доступными для использования остались регистры 18-21 и 30-31.

Подробности здесь: http://www.atmel.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_reg_usage.html

Иными словами, обязанность сохранения значения 12 регистов r18-r27, r30-r31 лежит на вызывающей программе. Обязанность сохранения значений 18 регистров r2-r17, r28-r29 лежит на вызываемой программе. Регистры r0 и r1 для переменных GCC не используются.

Так как в вызывающей функции не было ни аргументов, ни переменных, ей ничего сохранять в вышеприведенном примере не пришлось.

Неправильно в корне обясняете!!!

Каждая функция сохраняет на входе и востанавливает на выходе те регистры, которые сама и портит внутри себя. И все. Все просто. Что вызвало её или вызывается из неё - само за себя сохраняет. Не записываем -  не сохраняются. Регистры, использованные для передачи параметров и возврата - по другому.

А в целом да. Использование вызовов подпрограмм через INT вместо CALL применяют для оптимизации объема и скорости. На некоторых платформах это существенно, на АВР не очень. Еще это позволяет боротся с страничной адресацией, защищенными режимами и пр. Тоже не про нас.

  

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Нет. В последнем варианте разъяснений ptr - прав. В gcc принята "идиотская" (нестандартная) схема использования РОН. А именно так как им написано и указано в приведенной ссылке: call-used register -- регистры, задействованные ВЫЗЫВАЮЩИМ контекстом и CALL-SAVED register - регистры задействованные вызываемым контекстом.

То есть, реально-локальными переменными внутри вызванной функции могут быть ТОЛЬКО 2..17,28,29 регистры. Причем пара 28,29 практически всегда занята каким-то локальным указателем, часто генерируемым самим компилятором, то есть неявным. И именно о них она и заботится сохраняя внутри вызова.

Остальные регистры - по сути ВРЕМЕННЫЕ переменные, что хорошо видно в приведенном листинге (строка 05 в функции analogWrite) - идет казалось бы "лишнее" копирование из р24 в р17 .. а вот нифига не лишнее! Как только Вы объявляете "локал" - он первым делом занимает регистры из этой группы 2-17 (с конца), если не является указателем (28,29) .. даже если он пришел как "неизменяемый параметр" (р24). Потому как при наличии вызовов внутри функции, сюда будет записан параметр, и возможно будет возвращен результат, если потребуется. Это "дополнительные" затраты на функциональное программирование "с параметрами" тут. К вопросу в иной теме.

С другой стороны, такой подход позволяет компилятору проводить широкую оптимизацию кода над практически половиной регистрового пула и урезать типовое сохранение контекста практически вдвое .. за счет уменьшения реальных локалов тоже вдвое. И это - ПРАВИЛЬНО. Потому как основной способ доступа в SRAM - прямая адресация глобалов .. которые "не рекомендуются" :)

И тем не менее: так что вызов функции не сохраняет ВСЕ регистры, которые использует. Только половину, которая "call-saved". А вот обработчик прерывания ОБЯЗАН сохранять все регистры, которые использует.

Кстати, можно попробовать, но думаю что оно так и будет: если ногу назначить на Rx И одновременно разрешить с неё прерывание, то оно будет срабатывать, равно как и прерывание по получению байта .. просто "по умолчанию" прерывание выключено..

Кстати2, вывод на ногу и функции ввода с ноги объединены аппаратно для всех ног и функций, если следовать схеме, приведенной в даташитах. Это просто "фича такая" - следсвтвие "упрощения" схемотехники .. ну и может кому полезно. :)

a5021
Offline
Зарегистрирован: 07.07.2013

У нас чего тут, слет полуграмотных теоретиков? Хоть бы одному пришло в голову проверить, какую чепуху он тут несет. Ну давайте я сделаю это за вас. Итак, пишем скетч, где девятой ногой ардуины "шевелим" с помощью вызова функции, а десятой с помощью прерывания таймера. Сам код шевеления и внутри функции и внутри прерывания полностью идентичен:

void setup() {
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
}

void loop() {
  togglePin();
  delay(1000);
}

void togglePin(void) {
  if (digitalRead(9)) {
    digitalWrite(9, LOW);
  } else {
    digitalWrite(9, HIGH);
  }
}

ISR(TIMER1_OVF_vect) {
  if (digitalRead(10)) {
    digitalWrite(10, LOW);
  } else {
    digitalWrite(10, HIGH);
  }
}  

Тут все чисто и честно, надеюсь? Теперь внимательно смотрим на дизасемблер:

void setup() {
  pinMode(9, OUTPUT);
  ea:	61 e0       	ldi	r22, 0x01	; 1
  ec:	89 e0       	ldi	r24, 0x09	; 9
  ee:	0e 94 c8 01 	call	0x390	; 0x390 <pinMode>
  pinMode(10, OUTPUT);
  f2:	61 e0       	ldi	r22, 0x01	; 1
  f4:	8a e0       	ldi	r24, 0x0A	; 10
  f6:	0c 94 c8 01 	jmp	0x390	; 0x390 <pinMode>

000000fa <_Z9togglePinv>:
  togglePin();
  delay(1000);
}

void togglePin(void) {
  if (digitalRead(9)) {
  fa:	89 e0       	ldi	r24, 0x09	; 9
  fc:	0e 94 37 02 	call	0x46e	; 0x46e <digitalRead>
 100:	89 2b       	or	r24, r25
 102:	11 f0       	breq	.+4      	; 0x108 <_Z9togglePinv+0xe>
    digitalWrite(9, LOW);
 104:	60 e0       	ldi	r22, 0x00	; 0
 106:	01 c0       	rjmp	.+2      	; 0x10a <_Z9togglePinv+0x10>
  } else {
    digitalWrite(9, HIGH);
 108:	61 e0       	ldi	r22, 0x01	; 1
 10a:	89 e0       	ldi	r24, 0x09	; 9
 10c:	0c 94 01 02 	jmp	0x402	; 0x402 <digitalWrite>

00000110 <loop>:
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
}

void loop() {
  togglePin();
 110:	0e 94 7d 00 	call	0xfa	; 0xfa <_Z9togglePinv>
  delay(1000);
 114:	68 ee       	ldi	r22, 0xE8	; 232
 116:	73 e0       	ldi	r23, 0x03	; 3
 118:	80 e0       	ldi	r24, 0x00	; 0
 11a:	90 e0       	ldi	r25, 0x00	; 0
 11c:	0c 94 2a 01 	jmp	0x254	; 0x254 <delay>

ISR(TIMER1_OVF_vect) {
 120:	1f 92       	push	r1
 122:	0f 92       	push	r0
 124:	0f b6       	in	r0, 0x3f	; 63
 126:	0f 92       	push	r0
 128:	11 24       	eor	r1, r1
 12a:	2f 93       	push	r18
 12c:	3f 93       	push	r19
 12e:	4f 93       	push	r20
 130:	5f 93       	push	r21
 132:	6f 93       	push	r22
 134:	7f 93       	push	r23
 136:	8f 93       	push	r24
 138:	9f 93       	push	r25
 13a:	af 93       	push	r26
 13c:	bf 93       	push	r27
 13e:	ef 93       	push	r30
 140:	ff 93       	push	r31
  if (digitalRead(10)) {
 142:	8a e0       	ldi	r24, 0x0A	; 10
 144:	0e 94 37 02 	call	0x46e	; 0x46e <digitalRead>
 148:	89 2b       	or	r24, r25
 14a:	11 f0       	breq	.+4      	; 0x150 <__vector_13+0x30>
    digitalWrite(10, LOW);
 14c:	60 e0       	ldi	r22, 0x00	; 0
 14e:	01 c0       	rjmp	.+2      	; 0x152 <__vector_13+0x32>
  } else {
    digitalWrite(10, HIGH);
 150:	61 e0       	ldi	r22, 0x01	; 1
 152:	8a e0       	ldi	r24, 0x0A	; 10
 154:	0e 94 01 02 	call	0x402	; 0x402 <digitalWrite>
  }
}  
 158:	ff 91       	pop	r31
 15a:	ef 91       	pop	r30
 15c:	bf 91       	pop	r27
 15e:	af 91       	pop	r26
 160:	9f 91       	pop	r25
 162:	8f 91       	pop	r24
 164:	7f 91       	pop	r23
 166:	6f 91       	pop	r22
 168:	5f 91       	pop	r21
 16a:	4f 91       	pop	r20
 16c:	3f 91       	pop	r19
 16e:	2f 91       	pop	r18
 170:	0f 90       	pop	r0
 172:	0f be       	out	0x3f, r0	; 63
 174:	0f 90       	pop	r0
 176:	1f 90       	pop	r1
 178:	18 95       	reti

Вопросы есть, товарищи?

UPD. Забавно, но даже если не брать во внимание длинные последовательности сохранения/восстановления контекста, код ногодрыгания в функции работает быстрее, чем точно такой же код в прерывании. В строке 29 листинга видно, что компилятор вызывает функцию digitalWrite() не через call, а делает прямой переход на нее через jmp. Так как вызов выполняется четыре такта, а переход только три, то на этом экономится один такт. Но это не все. Благодаря такому трюку возврат из функции digitalWrite() через ret становится на самом деле возвратом из функции togglePin() в тело loop(). А вот в случае вызвова digitalWrite() из обработчика прерывания, этот же ret возвращает выполнение обратно в обработчик, а выход из обработчика осуществляется уже через reti, что влечет потерю еще четырех тактов на выполнение этой инструкции. Итого, даже без цепочек push/pop, один и тот же алгоритм в обработчике выполняется на пять тактов дольше, чем в функции.

ptr
Offline
Зарегистрирован: 28.05.2016

a5021 пишет:

Тут все чисто и честно, надеюсь?

Нет. Слишком сильно упростили, так что компилятору не пришлось сохранять регистры.

Берем сложнее пример:

static byte counter;
long some_garbage, intermediate;

void something(unsigned long a, unsigned long b, unsigned long c, unsigned long d);

void setup() {
  Serial.begin(115200);
  increment_counter();
}

void loop() {
  static unsigned long a = 1, b = 2, c = 3, d = 4;
  something(a++, b++, c++, d++);
  Serial.println(counter);
}

void increment_counter(void) {
  counter++;
}

ISR(TIMER1_OVF_vect) {
  counter++;
}

void something(unsigned long a, unsigned long b, unsigned long c, unsigned long d)
{
  register unsigned long e = 1, f = 2, g = 3, h = 4, i = 5, j=6;

  intermediate=e++ + f++ + g++ + h++ + i++ + j++ + a + b + c + d;
  increment_counter();     // <== Will commented
//  asm ("cbi 12,7");      // <== Will uncommented
  some_garbage+=intermediate+e-f+g-h+i-j+a-b+c-d;
}

Компилируем, сохраняем ассемблерный листинг. Потом комментарим выделенную строку, вызывающую функцию increment_counter(), убираем комментарий у ассемблерной комады, сбрасывающей бит в порту (эмулируя генерацию программного прерывания), снова компилируем и сохраняем ассемблерный листинг.

Сравниваем два листинга, чтобы сравнить затраты компилятора на вызов функции и на вызов прерывания. Видим различие в 6 строк. При этом 4 строки потратились на комментарии вокруг ассемблерной вставки. Итого на 10 строк больше в первом варианте.

Убрались строки:

	std Y+5,r18
	std Y+6,r19
	std Y+7,r20
	std Y+8,r21
...
	lds r24,intermediate
	lds r25,intermediate+1
	lds r26,intermediate+2
	lds r27,intermediate+3
...
	ldd r14,Y+1
	ldd r15,Y+2
	ldd r16,Y+3
	ldd r17,Y+4

Появились же строки

	movw r26,r16
	movw r24,r14

Итого, вместо двух команд суммарной длиной в четрые байта и выполняющихся за 2 такта, появилось 12 команд суммарной длиной 32 байта и выполняющихся 24 такта.

Для справедливости, следует отметить, что обработчик прерываний, написанный на C, по неведомым мне причинам сохранял и восстанавливал из стека регистр R1, который ему совершенно не нужен, а так же очищал R0, что тоже не требовалось. То есть зря потратил 6 байт флеша и 5 тактов. Второй момент, с которым я явно лоханулся, так это то, что забыл, что в AVR нельзя регистр флагов одной командой положить в стек и взять оттуда.

Поэтому обработчик прерываний, по сравнению с функцией потребовал дополнительно 11 команд, три из которых не нужны и лежат на совести GCC. Итого 22 байта и 19 такта.

Сухой остаток:

1. При использовании прерывания, написанном на C экономия составила 6 байт флеша и 3 такта.

2. При использовании прерывания, написанном на ассемблере экономия составила бы 12 байт флеша и 8 тактов.

 

a5021
Offline
Зарегистрирован: 07.07.2013

Вы кого тут собрались обмануть? Я не говорю сейчас о том, что вы написали полностью абсурдный код, который не выполняет никаких осмысленных действий. Я не имею ввиду, что вы вызываете прерывание взводом бита несуществующего регистра. У меня не вызывает кривую усмешку, что вы модифицируете переменную из прерывания без объявления ее volatile (кошмарный сон программиста). И я даже готов закрыть глаза на совсем уж полную дичь, в виде попытки отхватить двадцать четыре регистра под регистровые переменные (вы вообще о чем нибудь думали, когда такое писали?).

Все это семечки на фоне главного косяка -- изображенный вами мумифицированный труп не в состоянии будет удерживаться в вертикальном положении самостоятельно, если убрать хотя бы одну из многочисленных подпорок, которых вы тщательно понаставили в тайной надежде, что никто не заметит.

Объявите счетчик, как long и ваш труп завалится со страшным грохотом, превратившись в труху. Опустите increment_counter() в конец something() и труха станет зловонной жижей.

Ценой написания специально составленного, косячного и абсурдного кода, вы выиграли три такта. Мне пеняли на простоту моего скетча, но тут же сами изобразили максимально выхолощенный обработчик прерывания, чтобы не дай Бог там не случилось использвания хотя бы пары регистров и гигантский выигрышь (три такта) не обернулся проигрышем при прочих равных.

Вы на что вообще рассчитывали? Что тут все сплошняком дурачки и не заметят ваших примитивных подтасовок? Ну так обломайтесь. Данная туфта колется на раз. Не нужно даже быть профессиональным программистом, каковым я, кстати, и не являюсь. Вы хоть понимаете, что ваше железное доказательство, выполненное профессионалом, развалилось от легкого толчка любителя? Стыдоба то какая!

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Блин .. ваще как-бы почти "первый раз" слышу за экономию на вызове в виде ISR() нафига оно кому надо .. ни разу не встречал за 30 лет практики. А вот причину, покоторой Атмел так поступил - понимаю очень хорошо. На память, чиселки взяты с потолка, но что-то подобное видел в году так 91-93 .. уже не помню:

строка    код               сообщение
отчета   устройства
----------+-------------------+------------------------
1001        42003          Температура 10023, а 42002 и 42001 в 1525 грд.
                                  Вывод: 42003 неисправен, выставить счет фирме ...

1021        1038            обороты 20, U1040=24v, I1042=0A
                                  Вывод: 1038 оторван контакт на плате.
...

Это выдержка из отчета по запуску Шаттла. Так вот, чтобы иметь возможность полноценной диагностики и делают такие внутрикамневые самозамыкания входа на выход. Если я подал на выход сигнал и он вызвал прерывание с этой ноги, а внешнее устройство не откликнулось в заданное время - значит мой сигнал был, но до получателя НЕ дошел или если я подаю сигнал на ногу, а на входе нифига не вижу - значит моя нога уже отгорела и нефиг её дергать ..

ptr
Offline
Зарегистрирован: 28.05.2016

a5021 пишет:

Вы кого тут собрались обмануть?

Какая разница какой код, и что он делает в целях обсуждении вопроса о программных прерываниях?

Увидел, что был не прав, что может быть программное прерывание быстрее, но эго не позволило признать это, и стал говном исходить, выискивая к чем придраться, не имеющему вообще никакого отношения к обсудаемуму вопросу?

Противно, ей богу...

 

Logik
Offline
Зарегистрирован: 05.08.2014

Arhat109-2 пишет:

Нет. В последнем варианте разъяснений ptr - прав. 

Он неправ начиная с перевода "Calling C subroutines can clobber any of them - the caller is responsible for saving and restoring. - "обязанность сохранения значения...".  Нету там обязаности. Там "Ответственен". А это разное, ответственность предполагает принятие решения о необходимости и форме. Как правило ничего не сохраняется. Часть РОН задействованная под параметры согласно порядку передачи их сохранение и восстановление - очень специфично, остальная просто не задействована.

Arhat109-2 пишет:

В gcc принята "идиотская" (нестандартная) схема использования РОН. А именно так как им написано и указано в приведенной ссылке: call-used register -- регистры, задействованные ВЫЗЫВАЮЩИМ контекстом и CALL-SAVED register - регистры задействованные вызываемым контекстом.

Ниче там идиотского нет банальный компромис между локальными и параметрами с целью минимизации шанса полезть в стек для них. Р24-18 для параметров в строго заданом порядке, дальше в стек (ну еще 30-31, но я их лично не видел в такой роли). Сохранение и востановление очень специфично, как правило его нет вобще (особенно восстановления). Р2..17,28,29 - под локальные, дальше стек. Для них 100% "Каждая функция сохраняет на входе и востанавливает на выходе те регистры, которые сама и портит внутри себя. "

Arhat109-2 пишет:
То есть, реально-локальными переменными внутри вызванной функции могут быть ТОЛЬКО 2..17,28,29 регистры...Остальные регистры - по сути ВРЕМЕННЫЕ переменные, 

Чем  реально-локальные отличаются от ВРЕМЕННЫХ?  Игра слов. Те регистры что были использованы под параметры после того как станут невостребованы используются под локальные по полной. 

Arhat109-2 пишет:
 что хорошо видно в приведенном листинге (строка 05 в функции analogWrite) - идет казалось бы "лишнее" копирование из р24 в р17 .. а вот нифига не лишнее! Как только Вы объявляете "локал" - он первым делом занимает регистры из этой группы 2-17 (с конца), если не является указателем (28,29) .. даже если он пришел как "неизменяемый параметр" (р24). Потому как при наличии вызовов внутри функции, сюда будет записан параметр, и возможно будет возвращен результат, если потребуется. Это "дополнительные" затраты на функциональное программирование "с параметрами" тут. К вопросу в иной теме.

Ну в целом, так, но несколько тяжеловесно, что скрывает понимание. Нам надо, чтоб изменения pin в pinMode не вернулись в analogWrite. Для того и сделали копию в Р17. Если бы:

1. Предполагалось возвращение значения pin из pinMode.

2. Не предполагалось бы дальнейшее использование pin в analogWrite после вызова pinMode

то и сохранения не было бы. Вот потому и пишу что для регистров с параметрами - по другому, специфично и учитывать это в call&int вобще никак.

Arhat109-2 пишет:
 С другой стороны, такой подход позволяет компилятору проводить широкую оптимизацию кода над практически половиной регистрового пула и урезать типовое сохранение контекста практически вдвое .. за счет уменьшения реальных локалов тоже вдвое. И это - ПРАВИЛЬНО. Потому как основной способ доступа в SRAM - прямая адресация глобалов .. которые "не рекомендуются" :)

Ну это Вами надумано. См выше, все логично и просто.

Arhat109-2 пишет:
 

И тем не менее: так что вызов функции не сохраняет ВСЕ регистры, которые использует. Только половину, которая "call-saved"

Опятьдвадцатьпять ))) Ну где в примере сохранение половины?! Только 3 использованых.

Arhat109-2 пишет:
 

 А вот обработчик прерывания ОБЯЗАН сохранять все регистры, которые использует.

А вот и нет! Смысл сохранять те, которые не трогает обработчик?! Это чисто ардуиновская трабла, когда атачится обработчик, то неизвестно куда он нагадит, потому и сохранять приходится все. А не Вы ли мне рассказывали, что в новых версиях в обработчике как раз не все регистры сохраняют, оптимизируют?

По теме. Да, через прерывание немного короче и быстрей, но не существенно, внимания не достойно.

a5021
Offline
Зарегистрирован: 07.07.2013

ptr пишет:
Противно, ей богу...

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

Вы спрашиваете, "какая разница, какой код" и полагаете, что никто не догадался, что вы и сами давно уже знаете, какая? Каким же самомнением надо обладать, чтобы считать себя умелым ловчилой, способным с помощью примитивной горбухи обмануть кого угодно. По вашему же коду видно, как вы итерационным путем натягивали презерватив на глобус и подгоняли действие под результат. Ваш код не просто абсурдный. Он супер-абсурдный и содержит остатки предыдущих попыток, в которых ваши подтасовки не преблизили вас к желанному вами результату. Вы их даже убрать не сподобились.

Хотите конкретики? Пожалуйста. Я уже упоминал совершеннейшую дичь, когда в своем примере вы решили отхватить двадцать четыре регистра под регистровые переменные. Размышляя о том, как бы ловчее навести тень на плетень, вам подумалось, что если придушить компилятор по части использования им регистров, то ему придется больше напрягаться, распихивая по углам результаты промежуточных вычислений, которые он не успел закончить к моменту вызова функции, что приведет к дополнительным потерям во времени выполнения. Но компилятор на законных основаниях послал вас нахрен, чего вы, похоже, даже не поняли, но увидели отсутствие результата и вам пришлось искать другое решение. Зачем вы оставили полностью безсмысленное объявление регистровых переменных в исходном коде -- для меня полная загадка. Вам от незнания показалось, что это чем-то может вам помочь? Ну хоть бы посмотрели, сколько регистровых переменных вам выделил компилятор в ответ на вашу просьбу. Вы этого не сделали или не смогли. Тем не менее, это стало отпечатками пальцев на орудии преступления, уликой, доказывающая вашу полную причастность к неблаговидным действиям.

Понятно и то то, как вы двигались дальше. Вы решили принудить компилятор к искусственному переключению контекста перед вызовом процедуры, справедливо ожидая, что это вызовет накладные расходы по времени выполнения кода. Для этого вам пришлось сделать чудовищную, с точки зрения нормального программиста вещь -- рассечь блок громоздких вычислений, оперирующий большим количеством данных, надвое и в середину вставить вызов процедуры, которая бы обращалась к данным, не пересекающимися с ранее использованными. Т.е. это "эмуляция" прерывания в чистом виде. Зачем бы такое могло потребоваться в реальном коде, я как ни силюсь, представить себе не могу. Зато хорошо вижу, зачем это потребовалось вам. Очевидно, что к этому моменту вы уже отлично понимали, что прерывание -- это полная задница в плане потерь на переключение контекста и вы не нашли ничего умнее, как искусственно воспроизвести точь-в-точь механизм этих потерь, только теперь уже к собственной пользе. Именно для этого вызов функции был загнан в середину блока вычислений, хотя присутствие его именно там необъяснимо с точки зрения осмысленного подхода к программированию. Зато, хорошо иллюстрирует ваш потужный замысел -- доказать свое, хотя бы и ценой подлога.

Подлог у вас получился мертвый и уродливый. Блистательный образец говнокода в самом отвратительном его виде.

Logik пишет:
По теме. Да, через прерывание немного короче и быстрей, но не существенно, внимания не достойно.

Не получается короче и быстрей. По самой логике прерывания не получается. Не верите, посмотрите на конкретных примерах. Покопайтесь в дизасме, там все довольно наглядно. Чтобы уж совсем вопросов не осталось, пройдите путь дедушки-афериста -- напишите свой код, где бы вызов функции приводил к большим затратам, чем вызов прерывания. В процессе этого вы скорее всего поменяете свое мнение.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Logik, Вы невнимательны. Я как раз проэто и написал: "те, которые востребованы". Да gcc уже хорошо умеет оптимизировать работу со стеком.

В любом случае, спор не стоит выеденного яйца: вызывать функцию через аппаратное(!) прерывание .. можно конечно, но вот только зачем? Я вижу только одно место: "эмуляция механизма исключений". Но .. нету у AVR нормального количества памяти для того, чтобы это стало актуальным. Разве что на расширениях через X-bus..

ptr
Offline
Зарегистрирован: 28.05.2016

Arhat109-2 пишет:

Я вижу только одно место: "эмуляция механизма исключений". Но .. нету у AVR нормального количества памяти для того, чтобы это стало актуальным. Разве что на расширениях через X-bus..

Для связи с ядром мультизадачности может еще пригодится. Да и то, разве что на ATmega2560. Но стоимость ATmega2560 такова, что выгодней ESP взять и использовать Pro Mini в качестве контроллера портов для нее.

Опять меня этот демагог развел на спор, который действительно не стоит выеденного яйца. Пора его в игнор навеки )

 

a5021
Offline
Зарегистрирован: 07.07.2013

Научитесь признавать свои заблуждения. Тогда ни истерики закатывать, ни посылать никого никуда не потребуется. Сейчас же получается, что ни слив у вас, то концерт, а здесь все-таки не смотр художественной самодеятельности.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Atmega2560 - просто офигительный камень .. пользуем ещё первую нашу Ардуинку на нем .. перепилена, переклеена, перепаяна уже на 100500 раз, добавлены 16 отсутствующих ножек .. и всё нипочем! А как поначалу на неё дышать боялся .. :) И заюзать хотя бы половину флеша или большую часть периферии .. ну вот пока никак не получается. Как-то вот однажды SRAM не хватило, это да.

Сейчас вот закончу новую версию нашего "Лего-кирпича" с гребенками на 66 управляющих контактов по 3 выхода (GND, 5V, сигнал) и "встроенными" драйверами моторов и расширенной памятью - выложу. Должно получиться в габарите меньше(!) чем у Лего EV3 или NXT и с возможностью полного совпадения их соединительных мест с Лего! Афигеть.. только вот по высоте получается значительно больше из-за батарейного отсека на 18650 (+4мм) и нашего LCD1602 (+12мм) ..

Не, надо отметить что мои знакомые пожгли уже по паре плат .. как им это удалось - ни панимаю..

Извиняюсь за оффтоп .. ну ндравится мне этот камень .. не отнять.

ssss
Offline
Зарегистрирован: 01.07.2016

Arhat109-2 пишет:

Atmega2560 - просто офигительный камень ..

В двух словах, что именно, сможете?

ssss
Offline
Зарегистрирован: 01.07.2016

ptr пишет:

Опять меня этот демагог развел на спор, который действительно не стоит выеденного яйца. Пора его в игнор навеки )

Не дайте засохнуть моему попкорну! Доказывать ему что-то действительно бесполезно, но вы должны, вы просто обязаны! )))))))))))))))

findeler
Offline
Зарегистрирован: 08.03.2016

Мда, эпичненько.

Я на одно надеюсь, вы всё таки выяснили у кого длиннее.

Logik
Offline
Зарегистрирован: 05.08.2014

Канечно! Отсортировались по длине. Не застолбившимся вовремя присвоена нулевая длина ;)

a5021
Offline
Зарегистрирован: 07.07.2013

ssss пишет:
Доказывать ему что-то действительно бесполезно,

С вашими талантами полезно может быть лишь рыдать в подушку денно и нощно. Именно так вы лучше всего сможете уберечь себя от морального членовредительства, которым зачем-то занимаетесь в присутственных местах.

ssss
Offline
Зарегистрирован: 01.07.2016

a5021 пишет:

ssss пишет:
Доказывать ему что-то действительно бесполезно,

С вашими талантами полезно может быть лишь рыдать в подушку денно и нощно.

Угу! От смеха! Что я и делаю, периодически! )))))))))))))))))))))))))))))))))))))))))))))))

ssss
Offline
Зарегистрирован: 01.07.2016

Arhat109-2 пишет:

В любом случае, спор не стоит выеденного яйца: вызывать функцию через аппаратное(!) прерывание .. можно конечно, но вот только зачем? Я вижу только одно место: "эмуляция механизма исключений". Но .. нету у AVR нормального количества памяти для того, чтобы это стало актуальным. Разве что на расширениях через X-bus..

У AVR вообще ничего нет, что поделать. Даже в примитивном СТМ8 более-менее нормальный механизм прерываний и полноценный RESET.

 Management of hardware interrupts
– External interrupt capability on most I/O pins with dedicated interrupt vector and
edge sensitivity setting per port
– Peripheral interrupt capability
 Management of software interrupt (TRAP)
 Nested or concurrent interrupt management with flexible interrupt priority and level
management:
– Up to 4 software programmable nesting levels
– Up to 32 interrupt vectors fixed by hardware
– 2 non maskable events: RESET, TRAP
– 1 non-maskable top level hardware interrupt (TLI)

 

a5021
Offline
Зарегистрирован: 07.07.2013

ssss пишет:
От смеха! Что я и делаю, периодически!

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

ssss
Offline
Зарегистрирован: 01.07.2016

a5021 пишет:

ssss пишет:
От смеха! Что я и делаю, периодически!

Мне можете не рассказывать. Ваш фальшивый смех

Что? Абыдна, да? )))))))))))))))))

А ты не спорь в калашных рядах где лицом своим не вышел. )))))))))))))))))))

a5021 пишет:

Вы обречены "смеяться" таким образом постоянно

Канэшна! Знай с кем спорить и на какую тему, чтобы потом фэйсом в луже не пузырить. ))))))))))))))))))))))

Соблюдай цветовую субординацию штанов, кароче!