Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: IAR EWAVR - как избавиться от лишних команд?
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > IAR
kv_addr
В критичных по времени функциях использую зарезервированные РОН из нижней половины, в нижеуказанном примере r14 и r15:
CODE
...
volatile __regvar __no_init char Old_PINB @14;
volatile __regvar __no_init char New_PINB @15;
...
Old_PINB = New_PINB;
New_PINB = PINB;
...


Получаю в результате:
CODE
// 70 Old_PINB = New_PINB;
MOV R14, R15
// 71 New_PINB = PINB;
IN R16, 0x03
MOV R15, R16

В вышеуказанном примере последовательность команд IN R16,0x03 и MOV R15,R16 явно лишняя, достаточно было одной команды IN R15,0x03. Расходуется лишняя пара байтов и лишний такт. Казалось бы мелочь, но такого при работе с зарезервированными под переменные регистрами может набежать вполне прилично.

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

Мог бы кто нибудь подсказать, как в EWAVR это можно побороть, или сия бага есть врожденная фича EWAVR?
Xenia
Когда переменная объявлена с адресом памяти (например @14), то и компилятор использует ее, как память, даже тогда, когда адрес настолько мал, что налезает область на регистров.
Если хотите, чтобы переменная использовалась как регистр, то объявите ее дефайном, как синоним имени нужного вам регистра. Например так:
#define Old_PINB PORTE
#define New_PINB PORTF
Имена регистров ищите в хидере, соответствующему вашему типу МК.
kv_addr
Цитата(Xenia @ Aug 27 2011, 19:25) *
Когда переменная объявлена с адресом памяти (например @14), то и компилятор использует ее, как память, даже тогда, когда адрес настолько мал, что налезает область на регистров.
Если хотите, чтобы переменная использовалась как регистр, то объявите ее дефайном, как синоним имени нужного вам регистра. Например так:
#define Old_PINB PORTE
#define New_PINB PORTF
Имена регистров ищите в хидере, соотвествующему вашему типу МК.

Вот здесь либо я Вас не понимаю, либо Вы меня. Поэтому постараюсь объяснить подробнее. В пункте Register Utilization я могу зарезервировать от одного (R15) до 12 (R4...R15) операционных регистров под глобальные переменные. Поскольку работать с переменными, расположенными в регистрах, можно быстрее, чем с переменными расположенными в ячейках ОЗУ, в критичных по времени функциях пытаюсь использовать такую возможность, что в моем случае вполне оправдано. Но компилятор генерирует не совсем оптимальный код, пример которого привел выше.

Объясните, пожалуйста, если я хочу старое и новое значения байтов, полученных из порта PINB, хранить именно в РОН и дальше в функции работать именно с данными в этих регистрах (естественно, учитывая ограничения для нижних 16 РОН у AVR), то чем Ваши вышеуказанные дефайны могут мне помочь? У меня Old_PINB и New_PINB являются однобайтовыми глобальными переменными, расположенными в регистрах R14 и R15 соответственно, а отнюдь не регистры портов PORTE и PORTF. Я ведь спрашивал, как, например, получить байт из порта B непосредственно в тот же регистр R15 (пересылка байта из порта в регистр возможна для всех 32 регистров, а не только верхней их половины), не привлекая сюда избыточно регистр R16.

Например, в подпрограмме обработки прерывания мне нужно получить новое значение, предварительно сохранив предыдущее, как старое. На ассемлере это "звучало" бы так:
CODE
...
MOV R14,R15
IN R15,PINB
RETI

А компилятор порождает код:
CODE
...
ST -Y, R16
MOV R14,R15
IN R16,PINB
MOV R15,R16
LD R16,Y+
RETI

А зачем мне эти лишние байты и лишние такты?
Xenia
Цитата(kv_addr @ Aug 27 2011, 20:15) *
В пункте Register Utilization я могу зарезервировать от одного (R15) до 12 (R4...R15) операционных регистров под глобальные переменные. Поскольку работать с переменными, расположенными в регистрах, можно быстрее, чем с переменными расположенными в ячейках ОЗУ, в критичных по времени функциях пытаюсь использовать такую возможность, что в моем случае вполне оправдано. Но компилятор генерирует не совсем оптимальный код, пример которого привел выше.

Резервирование это одно, а компиляция через инструкцию IN - другое. Резервирование означает только то, что компилятору запрещается использовать эти регистры при генерации C-кода (причем за библиотеки компилятор отвественности не несет, т.к. получает их уже скомпилированными). Нечего кроме этого операция резервирование не означает. Т.е. только от этого память не станет регистром, а регистр не станет памятью.

С точки зрения компилятора регистр отличается от памяти способом адресации. Хотя с точки зрения электронщика это может быть одно и тоже sm.gif. В отношении регистра компилятор использует команды IN и OUT, тогда как к памяти обращается по ее адресу. В этом и состоит это отличие.

Регистры объявлены в хидере с помощью определения SFR_B или ему подобных, описанных в iomacro.h. Эти определения сильно компиляторозависимые, а потому не стоит повторять те приемы, которыми они там определятся как регистры, хотя самим SFR_B пользоваться вполне можно. Например так:
SFR_B(Old_PINB, 14)
SFR_B(New_PINB, 15)
Но лучше, все-таки так не делать, а найти в хидере уже определенные имена регистров из области 14-го и 15-го байта. Там должно быть что-то вроде этого:
SFR_B(?????, 0xE)
SFR_B(?????, 0xF)
Вот с этими именами, которые у меня записаны, как "?????", и нужно определить тождество через #define и тогда такие клоны тоже будут использоваться компилятором, как регистры.

Попытки же использовать зарезервированное среди регистров место по адресу (@14 или @15) приводит лишь к тому, что это место используется, как память. Т.е. обращение к нему будет происходить по адресам 14 или 15, а не регистровыми опрациями IN/OUT.

Цитата(kv_addr @ Aug 27 2011, 20:15) *
Объясните, пожалуйста, если я хочу старое и новое значения байтов, полученных из порта PINB, хранить именно в РОН и дальше в функции работать именно с данными в этих регистрах (естественно, учитывая ограничения для нижних 16 РОН у AVR), то чем Ваши вышеуказанные дефайны могут мне помочь?

Дефайны помогают тем, что отожествляют ваши имена Old_PINB и New_PINB с именами регистров, а не с памятью. А это позволяет обращаться к ним, как к регистрам, используя команды IN/OUT. А ваше определение этих имен, как переменных, с указанными для них адресами (@14 или @15), является определением для ячеек памяти. По этой причине компилятор в их отношении команды IN/OUT не использует.

Я понимаю, что вы хотели бы, чтобы компилятор сам догадался, что по адресам 14 и 15 находятся регистры. Однако догадаться об этом он не может. Причина в том, что эти адреса считаются от начала сегмента кода! А где начинается сегмент кода знает только линкер, а компилятор этого не знает и знать не должен. Поэтому в данном случае вина лежит целиком на вашем корявом программировании, а не на компиляторе замечательной фирмы IAR. sm.gif

Цитата(kv_addr @ Aug 27 2011, 20:15) *
У меня Old_PINB и New_PINB являются однобайтовыми глобальными переменными, расположенными в регистрах R14 и R15 соответственно, а отнюдь не регистры портов PORTE и PORTF.

А в моем AT90USB647 по адресам 14 и 15 находятся именно регистры PORTE и PORTF! Вы ведь поленились назвать тип вашего МК? А раз так, то не имеете права выставлять мне претензий за то, что я ваши 14-ю и 15-ю ячейки неправильно отнесла. Телепатическими способностями в отношении вас я не обладаю, поэтому в своем прошлом ответе ограничилась вежливой формулировкой "имена регистров ищите в хидере, соответствующему вашему типу МК". Почему бы вам и в самом деле не посмотреть, как называются у вашего МК регистры, находящиеся в 14-ом и 15-ом байтах?
Палыч
Цитата(Xenia @ Aug 27 2011, 21:12) *
от этого память не станет регистром, а регистр не станет памятью.
Но в AVR разработчики МК постарались, чтобы к General Purpose Working Registers можно было бы обращаться двояко: как к регистрам и как к ячейкам памяти. Якобы, это сделано для того, чтобы удобнее было бы разработчикам компиляторов, но, вот, разработчики эту возможность не используют и не учитывают (или, точнее сказать, не в полной мере)...

Цитата(Xenia @ Aug 27 2011, 21:12) *
А в моем AT90USB647 по адресам 14 и 15 находятся именно регистры PORTE и PORTF!
Маленькая путаница с понятием "регистр" возникла из-за того, что в AVR два типа регистров:
1.General Purpose Working Registers
2. I/O Registers
Вы, Xenia, говорите об одних регистрах, ТС - о других...
kv_addr
"Девушка, пожалуйста, не кричите очень громко, я Вас и так неплохо слышу"©1111493779.gif sm.gif

Цитата
А в моем AT90USB647 по адресам 14 и 15 находятся именно регистры PORTE и PORTF! Вы ведь поленились назвать тип вашего МК?


Если Вас интересует контроллер, то в данном конкретном случае это ATmega88, хотя может быть и любой иной из атмеловских 8-биток, в том числе и Ваш AT90USB647. У них у всех имеются одни и те же 32 восьмибитных регистра общего назначения (РОН), по-импортному они еще называются General Purpose Working Registers. Ну, те самые, которые от R0 до R31 принято именовать.
Вы, очень похоже, спутали их с регистрами ввода/вывода (РВВ), по-импортному I/O Registers, кои в общем адресном пространстве находятся над РОН. Видимо, ввиду атмеловских безобразий с нумерацией регистров и возникло это недоразумение. Тот же Ваш горячо любимый PORTE гады атмелцы нумеруют как 0x0E (0x2E), а PINF (не PORTF, тут Вы ошиблись) как 0x0E (0x2E).

Дык, вот, как Вы могли уже заметить, я не собирался и не собираюсь использовать ни PORTE, ни даже PINF для хранения регистровых глобальных переменных, у меня запросы несколько скромнее:

Нажмите для просмотра прикрепленного файла

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

Что же касательно Вашего предположения относительно того, что компилятор считает зарезервированный под переменную РОН как ячейку памяти, в которую напрямую без дополнительного РОН из старших 16, которые обычно используются компилятором в качестве локальных (в примере это R16), невозможно положить содержимое того же регистра ввода/вывода PINB, то может такое и можно было бы заподозрить, если бы для передачи байта из одного зарезервированного РОН в другой точно так же задействовался бы дополнительный РОН-костыль.

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

Но ведь на самом деле в скомпилированном коде нет последовательности MOV R16,R15 MOV R14,R16 или нет даже попытки задействовать команды LD и ST, а байт передается одной вполне логичной командой MOV R14,R15. Т.е. здесь компилятор не пытается почесать левое ухо правой ногой.

Так почему он боится напрямую передать байт из РВВ в РОН вполне логичной штатной командой, а пристегивает сюда еще один, явно не нужный РОН R16? Можно ли избежать этой несуразности компилятора?

Надеюсь, я вполне внятно сформулировал вопрос?
Xenia
Цитата(kv_addr @ Aug 27 2011, 22:56) *
Так почему он боится напрямую передать байт из РВВ в РОН вполне логичной штатной командой, а пристегивает сюда еще один, явно не нужный РОН R16? Можно ли избежать этой несуразности компилятора?

Поставьте режим оптимизации, хотя бы в минимальной степени, и этот эффект исчезнет.
kv_addr
Цитата(Xenia @ Aug 27 2011, 23:00) *
Поставьте режим оптимизации, хотя бы в минимальной степени, и этот эффект исчезнет.

Уже... И неоднократно... И с разными способами и уровнями оптимизации... Врёт EWAVR во всех случаях, как сивый мерин. sad.gif

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

Вот такие пироги с котятами... 05.gif
Палыч
Цитата(Xenia @ Aug 27 2011, 23:00) *
... эффект исчезнет.
Нет, не исчезнет... В данном случае: либо смириться с таким поведением компилятора, либо (раз уж начали использовать нестандартные средства) пойти дальше - использовать ассемблерные вставки.
kv_addr
Цитата(Палыч @ Aug 27 2011, 23:19) *
Нет, не исчезнет... В данном случае: либо смириться с таким поведением компилятора, либо (раз уж начали использовать нестандартные средства) пойти дальше - использовать ассемблерные вставки.

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

Правда, интересно было бы узнать, может кто-то смог побороть эту "фичу"?
zhevak
Цитата(kv_addr @ Aug 28 2011, 01:31) *
Собственно говоря, я хотел выяснить, является ли возможным образумить компилятор. И если да, то каким образом.
Если же нет, это тоже не так уж страшно, рисуем ассемблерные модули. Для себя делаем заметку - имеется бага компилера и это есть его встроенная фича.

Правда, интересно было бы узнать, может кто-то смог побороть эту "фичу"?

Ситуацию понял. Но по сути вопроса мне сказать ничего, поэтому прошу прощения за оффтоп.

Перфекционизм -- это плохо. Иногда нужно просто решать задачу. Любым способом.
Решить ее и двигаться дальше. Но когда насущная задача в подсознании подменяется задачей "красиво" решить, то имеется нехилая вероятность зависнуть на ней и потерять кучу времени. А потом, наверстывая упущенное, наговнокодить. Единственный совет наверно может быть такой -- не бойтесь написать говнокод. Вы ведь знаете, что это есть говонокод -- а это главное. Конечно, по возможности нужно избегать такой практики, но жизнь есть жизнь и не всегда получается срать красиво. (Простите и меня за мой хранцузский). Не надо бояться говокода там, где он не влияет на конечный результат. Что касается меня, то я обычно в таких случая ставлю в своих исходниках предупреждающие значки типа

CODE
// TODO: Причеши бабушкин код

или
CODE
// TODO: Не забудь убрать за собой свои засранки

или
CODE
// TODO: Проблема решена с помощью тупого быдлокода.


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

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

kv_addr
Цитата(zhevak @ Aug 28 2011, 12:08) *
Ситуацию понял. Но по сути вопроса мне сказать ничего, поэтому прошу прощения за оффтоп.
Перфекционизм -- это плохо. Иногда нужно просто решать задачу. Любым способом.
...
Потом, когда появляется свободное время можно будет и поупражняться в перфекционизме.
Программисты -- они всё еще в душе художники. Но программирование -- уже давно не искусство, а ремесло.
Соблюдайте баланс между искусством и ремеслом в своей работе и не позволяйте ситуации управлять вами!

sm.gif
Хмм... Отвечу предельно просто. Решаю проблему всеми возможными средствами. Предпочитаю решать по возможности изысканно. Если так не получается, решаю менее "красивым" способом, лишь бы конечный результат был ВЕРНЫМ.
Если по ходу дела встретился вопрос, решение которого сулит на даный момент и впредь более "красивый" вариант решения, не премину попытаться с ним разобраться, если он решаем, этим решением буду пользоваться в дальнейшем. Но без особого фанатизма. Если получаю отрицательный ответ, ставлю заметку на сей счет и еду дальше, решая проблему иным образом. Вроде так.

Кстати, подбив все за и против, критичные по времени прерывания рисую на ассемблере. Мне это как бы и не очень страшно. sm.gif

PS: А вообще-то я в большей мере электронщик, чем программист, просто иногда приходится кой-чего выполнять на контроллерах, ну и соответственно писать для них программы, т.е. программирование есть дополнение к основной профессии.
Xenia
kv_addr, можно попросить вас выложить проектик для испытаний? Ну чтобы там и регистры зарезервированы были, и вот эти строчки:
Цитата
volatile __regvar __no_init char Old_PINB @14;
volatile __regvar __no_init char New_PINB @15;
...
Old_PINB = New_PINB;
New_PINB = PINB;
...

Больше ничего не надо, лишь бы компилировалось без ошибок.
А то уж больно не хочется самой проект городить, тем более что потом не докажу, что компилится он нормально.
kv_addr
Цитата(Xenia @ Aug 28 2011, 18:14) *
kv_addr, можно попросить вас выложить проектик для испытаний? Ну чтобы там и регистры зарезервированы были, и вот эти строчки:
Больше ничего не надо, лишь бы компилировалось без ошибок.
А то уж больно не хочется самой проект городить, тем более что потом не докажу, что компилится он нормально.

Нет ничего проще, там же и все пояснения:
Код
/*******************************************************************************
* Файл: Test.c
* Контроллер: ATmega88
* Тест для проверки результата компиляции. Ничего лишнего.
* В категории C/C++ Compiler в закладке Code в пункте Register utilization
* установить резервированными 2 регистра R14 и R15.
* Выбирать различные способы и уровни оптимизации, чтобы удостовериться в том,
* что компилятор будет тупо задействовать R16, а то и R17 в подпрограмме
* обработки прерывания, где достаточно было бы только три команды:
  MOV R14, R15
  IN R15, PINB
  RETI
* :-)
*******************************************************************************/
#include <ioavr.h>
#include <inavr.h>
#include <stdlib.h>

volatile __regvar __no_init char Old_PINB @14;
volatile __regvar __no_init char New_PINB @15;

//------------------------------------------------------------------------------
// Pin Change Interrupt (Port B)
//------------------------------------------------------------------------------
#pragma vector= PCINT0_vect
__interrupt void Pcint0(void)
{
  Old_PINB = New_PINB;
  New_PINB = PINB;
}

//------------------------------------------------------------------------------
// Главная программа
//------------------------------------------------------------------------------
void main(void)
{
  PCICR = (1<<PCIE0);
  PCMSK0 = 0xFF;
  Old_PINB = 0;
  New_PINB = 0;
  __enable_interrupt();
  for(;;)
  {
   // Сюда можно что-то вставить, а можно и ничего.;-)  
  }
}
_Bill
Цитата(kv_addr @ Aug 27 2011, 18:37) *
В критичных по времени функциях использую зарезервированные РОН из нижней половины, в нижеуказанном примере r14 и r15:
CODE
...
volatile __regvar __no_init char Old_PINB @14;
volatile __regvar __no_init char New_PINB @15;
...
Old_PINB = New_PINB;
New_PINB = PINB;
...


Получаю в результате:
CODE
// 70 Old_PINB = New_PINB;
MOV R14, R15
// 71 New_PINB = PINB;
IN R16, 0x03
MOV R15, R16

В вышеуказанном примере последовательность команд IN R16,0x03 и MOV R15,R16 явно лишняя, достаточно было одной команды IN R15,0x03. Расходуется лишняя пара байтов и лишний такт. Казалось бы мелочь, но такого при работе с зарезервированными под переменные регистрами может набежать вполне прилично.

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

Мог бы кто нибудь подсказать, как в EWAVR это можно побороть, или сия бага есть врожденная фича EWAVR?
Скорее всего это фича. Все дело в том, что младшие регистры r0-r15 не могут быть использованы в некоторых инструкциях, например в ldi. Разработчики компилятора приняли соглашение, что загрузка констант в эти регистры происходит через рабочие регистры.Соответственно и чтение из портов В/В также выполняется через рабочие регистры.
kv_addr
Цитата(_Bill @ Aug 28 2011, 21:51) *
Скорее всего это фича. Все дело в том, что младшие регистры r0-r15 не могут быть использованы в некоторых инструкциях, например в ldi. Разработчики компилятора приняли соглашение, что загрузка констант в эти регистры происходит через рабочие регистры.Соответственно и чтение из портов В/В также выполняется через рабочие регистры.

Все же, похоже, это - багофича. Потому как:
Код
#pragma vector= PCINT0_vect
__interrupt void Pcint0(void)
{
  Old_PINB = New_PINB;
  New_PINB = PINB;
  PORTB = Old_PINB;
  DDRB = New_PINB;
}

дает следующее:
Код
        ST      -Y, R16
//   28   Old_PINB = New_PINB;
        MOV     R14, R15
//   29   New_PINB = PINB;
        IN      R16, 0x03
        MOV     R15, R16
//   30   PORTB = Old_PINB;
        OUT     0x05, R14
//   31   DDRB = New_PINB;
        OUT     0x04, R15
//   32 }
        LD      R16, Y+
        RETI


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

PS: Если попробовать любые порты ввода-вывода, для которых применимы не только IN и OUT, но также и LDS и STS, вывод идет без посредника, а ввод только через регистр-костыль. Что-то айаровцы в этом наглючили.
Xenia
Цитата(kv_addr @ Aug 28 2011, 20:28) *
Нет ничего проще, там же и все пояснения:

Мне надо не это, а файл проекта. Т.е. то, что идет с расширением eww и ewp. Самое простое - закать в архив все целиком и приложить к сообщению. Это нужно затем, чтобы проверить, с какими опциями проходила компиляция.

Дело еще и в том, что меня тоже в свое время раздражало, что компилятор всё передавливает через промежуточный регистр R16, но после того, как установила оптимизацию medium по size, компилировать стало нормально. Поэтому и с вашим проектом я хотела поиграть режимом оптимизации, тем более что вы мне так не ответили (сообщение #7), проверяли ли вы влияние оптимизации на этот эффект или нет.
_Bill
Цитата(kv_addr @ Aug 28 2011, 22:34) *
.
Это не противоречит тому, что я написал выше. Младший регистр находится в правой части выражения, т.е. является источником данных.
kv_addr
Цитата(Xenia @ Aug 28 2011, 23:17) *
Мне надо не это, а файл проекта. Т.е. то, что идет с расширением eww и ewp. Самое простое - закать в архив все целиком и приложить к сообщению. Это нужно затем, чтобы проверить, с какими опциями проходила компиляция.

Дело еще и в том, что меня тоже в свое время раздражало, что компилятор всё передавливает через промежуточный регистр R16, но после того, как установила оптимизацию medium по size, компилировать стало нормально. Поэтому и с вашим проектом я хотела поиграть режимом оптимизации, тем более что вы мне так не ответили (сообщение #7), проверяли ли вы влияние оптимизации на этот эффект или нет.

И это проблемы не составит. Попробуйте, посмотрите.
По поводу оптимизации я вроде бы отвечал, перебрал все варианты. Бесполезно.
Палыч
Цитата(Xenia @ Aug 28 2011, 23:17) *
с вашим проектом я хотела поиграть режимом оптимизации

Было бы интересно узнать: удалось ли Вам с помощью настроек проекта исключить этот эффект?
kv_addr
Цитата(Xenia @ Aug 28 2011, 23:17) *
Мне надо не это, а файл проекта. Т.е. то, что идет с расширением eww и ewp. Самое простое - закать в архив все целиком и приложить к сообщению. Это нужно затем, чтобы проверить, с какими опциями проходила компиляция.

Дело еще и в том, что меня тоже в свое время раздражало, что компилятор всё передавливает через промежуточный регистр R16, но после того, как установила оптимизацию medium по size, компилировать стало нормально. Поэтому и с вашим проектом я хотела поиграть режимом оптимизации, тем более что вы мне так не ответили (сообщение #7), проверяли ли вы влияние оптимизации на этот эффект или нет.

И как успехи?
Xenia
Цитата(kv_addr @ Sep 2 2011, 17:41) *
И как успехи?


Никак. Получается то же, что и у вас: OUT нормально, IN через R16. По-видимому, это не лечится.
kv_addr
Цитата(Xenia @ Sep 2 2011, 19:04) *
Никак. Получается то же, что и у вас: OUT нормально, IN через R16. По-видимому, это не лечится.

Собственно говоря, иного и не ожидал. В любом случае это стало той последней каплей, которая перевесила часу весов в пользу написания критичного по времени прерывания на ассемблере. При наихудшем стечении обстоятельств ему отводилось менее 64 тактов. На си получалось при любых ухищрениях все же больше, на ассемблере в худшем случае не более 52 на все про все. Заодно поставил галочку на одной из "особенностей" EWAVR-а.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.