Оптимизация, быстродействие кода

sadman41
Offline
Зарегистрирован: 19.10.2016

Если взять 10 шт. if() и 10 шт. case и измерять время выполнения рутины при сработке третьего по счету (в исходнике) условия, то оно может быть больше в случае с case, т.к. компилятор равновероятно соптимизирует код, запихав это 3-е условие в конец таблицы переходов.

SAB
Offline
Зарегистрирован: 27.12.2016

У элементов логики фронты импульсов бывают больше 10 микросекунд. Не говоря уже об механики аппарата, который будет осуществлять работу. Или это просто какая то идеальная система, разработанная 20 лет назад. Такое впечатление, что автор теоретик, пишущий очередную диссертацию, ради того, чтобы было. 20 монет в секунду, да ещё и с сортировкой фальшивых, это фантастика. Может быть нам автор всё же раскроет секрет, где работает двадцать лет данная система? И для чего её всё же решили усовершенствовать? А так всё это жесткий тролинг форума IMHO.

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

wdrakula пишет:

ОК.

Давай начнем.

1. Просто замечание. Я увидел, что ты интересуешься ассемблерными вставками. Вот тебе ссылка:

http://microsin.net/programming/avr/avr-gcc-inline-assembler.html

но я бы не советовал погружаться туда глубоко. Компилятор сделает код ТОЧНО быстрее, чем неопытный человек. Чтобы "обнулить" переменную нужно записать по её адресу 0, если переменная однобайтовая или несколько нолей, по размеру переменной. С учетом того, что компилятор один регистр оставляет для константы 0, каждая запись в ОЗУ - 2 такта. При компиляции программы, если переменная не имеет глобального применения, компилятор часто размещает её в регистре. Тогда все меняется.

Вывод из п.1. - не стоит городить ассемблерные вставки, тем более если ты не знаешь РИСК-ассемблера и особенности компилятора. Ты потратишь очень много времени на обучение.

2. Теперь к сути задачи: При тестировании получаем примерно 10 (пусть 20) импульсов по не более 10 мкс, так? На все меньше 500 мкс, так? Сама идея ожидать паузу между импульсами, вызывая прерывания каждые 10 мкс - порочна. в самом минимуме такое прерывание (аккуратно написанное) займет от 5 мкс, то есть ПОЛОВИНУ процессорного времени, на остальную работу ничего не останется.

В этом случае делают так: 1 вариант - остановить (Обязательно!) другие прерывания, так как в АВР нет приоритета прерываний, на время тестирования и в этот момент не делать, кроме ожидания прекращения импульсов, ВООБЩЕ НИЧЕГО.

2 вариант, более правильный: смотрим по таблице ожидаемых результатов и видим, что (к примеру) 20 импульсов уже точно не соответствуют никакой подлинной монете. Значит мы можем посчитать импульсы на 200 мкс и сравнить полученное количество с таблицей, так? Импульсы нужно считать таймером, если мы не хотим запрещать прерывания. 200 мкс можно просто отмерить встроенной функцией micros(). 200-500 мкс не заметит никакая другая часть твоей программы. Ни дисплей, ни вывод в сериал, при правильном программировании. Монета в тракте за пол миллисекунды сместится максимум на 1 мм.

Этот прием в программировании используется часто. Мы заранее жертвуем часть ресурса, в данном случае фиксируем максимальное время проверки и пренебрегаем возможностью закончить тестирование раньше, поскольку трата ресурсов на отслеживание точного завершения проверки в десятки раз больше, чем просто зафиксировать время теста в, скажем, 300 мкс.

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

Понял ли и сможешь ли написать такое сам?

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

ЗЫ: я выше по тексту увидел, как ты заявил, что switch быстрее серии  else if. Для АВР это совершенно наоборот. Видно, что ты неплохо знал особенности работы на обычном ПК. Там другая архитектура и там выбор switch case был верен.

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

Вопрос такой, (так как пока не смотрел ассемблерный код, сгенерированный компилятором, который пока не знаю как посмотреть):

Допустим есть команда 

Coin_OK = false;

Если я такие и подобные команды буду заменять ассемблерными вставками такими как 

asm volatile ("clr %0" : "=r" (Coin_OK));

не означает что я буду казаться хотеть умнее компилятора, то что он и так такие ассемблерные команды вставляет вместо Си-шных команд? Мне кажется, что так оно и есть. Но хотя мысли такие, что, допустим, смотрим в цикле переменную и, если она равна 0, то максимально быстро (меняем уровень пина порта работы двигателя) останавливаем двигатель такой вот командой

asm volatile("sbi %0, 0x03" :: "I" (_SFR_IO_ADDR(PORTB)));

Я к тому, что вроде и пишу вместо Ардуиновских 

digitalWrite()

вот так 

PORT_WORK &=  ~(MOTOR_REVERSE | MOTOR_COUNTER)

но не знаю всю "поднаготную", как прошивает компилятор, какими командами после компиляции мою Ардуину.

"При компиляции программы, если переменная не имеет глобального применения, компилятор часто размещает её в регистре. Тогда все меняется."

Про это я бы потом с Вами поговорил, мне это пока непонятно.

2) Далее, я тоже прихожу к выводу, что 10 мкс это очень мало. Про 10 мкс мне сказали здесь, что программа машины проверяет именно через такое время. Я и уперся в это число, но по Вашим словам прихожу к выводу что можно увеличить это время. Нужно ещё раз перелопатить логику программы. Программа большая по моим меркам, для меня это первый серьёзный проект. 

Ваши 2 варианта алгоритма работы я продумаю, попробую что-то поменять. Плохо то, что у меня пока есть только код, датчики ещё даже не подключал, схема не разработана. Сделал пока меню на дисплее, чтобы всё правильно работало, и пока имитация подсчёта монет через случайные данные функцией random().

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

И ещё, я мало знаю в ПК, программировал в Дельфи давно. А разницу работы switch | if-else, взял из сайта Гайвера

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

SAB пишет:

У элементов логики фронты импульсов бывают больше 10 микросекунд. Не говоря уже об механики аппарата, который будет осуществлять работу. Или это просто какая то идеальная система, разработанная 20 лет назад. Такое впечатление, что автор теоретик, пишущий очередную диссертацию, ради того, чтобы было. 20 монет в секунду, да ещё и с сортировкой фальшивых, это фантастика. Может быть нам автор всё же раскроет секрет, где работает двадцать лет данная система? И для чего её всё же решили усовершенствовать? А так всё это жесткий тролинг форума IMHO.

Парни, я же писал просьбу не писать подобные заявления. ЗАО НПП "АДОНИС". Продукция банковская техника - АСМ

https://promkat.ru/schetchik-monet.htm

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

AlexBajdin59rus пишет:

 который пока не знаю как посмотреть):

/путь к ИДЕ/hardware/tools/avr/bin/avr-objdump -S твой скетч.elf  > xxxxx.S

где твой скечт.elf - елф-файл в tmp директории, где ИДЕ собирает проекты

xxxxx.S придумай имя файла для вывода и смотри свой ассемблерный код

AlexBajdin59rus пишет:

asm volatile ("clr %0" : "=r" (Coin_OK));

clr очистка РЕГИСТРА! Не любой переменной. Риск архитектура, да ещ Гарвардская сильно отличается от привычного тебе асма ПК.

AlexBajdin59rus пишет:

Я к тому, что вроде и пишу вместо Ардуиновских 

digitalWrite()

вот так 

PORT_WORK &=  ~(MOTOR_REVERSE | MOTOR_COUNTER)

Вот так и пиши. Это и правильно и оптимально. Если нет ситуации, критичной по времени - пользуйся digitalWrite(). Есть 100500 способов сделать ошибку в коде, не плоди новых. В твоей задаче НИГДЕ не нужно гнаться за тактами, честно. :)) Ты просто немножко неправильно подошел.

AlexBajdin59rus пишет:

"При компиляции программы, если переменная не имеет глобального применения, компилятор часто размещает её в регистре. Тогда все меняется."

Про это я бы потом с Вами поговорил, мне это пока непонятно.

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

AlexBajdin59rus пишет:

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

И ещё, я мало знаю в ПК, программировал в Дельфи давно. А разницу работы switch | if-else, взял из сайта Гайвера

Гайвер - воплощенное ЗЛО! ;))))) Пример популяризация "себя любимого" без какого либо понимания предмета.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

AlexBajdin59rus пишет:
но может пока ищу, Вы мне быстрее подскажите как например выставить на порту В пин в высокий уровень...?

Пробую так:

asm volatile( "sbi PORTB, 3" );

не получается...

Какой пин Вы здесь выставляете?

AlexBajdin59rus пишет:

Пробую так - acm volatile (" clr var");

Не получается...

Понятно.

AlexBajdin59rus пишет:

как мне ...

Для начала, изучить систему команд процессора для которого пишете

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

b707 пишет:

Logik пишет:

основная проблема тут не в этом такте, а в сохранении и восстановлении регистров при входе выходе в прерывание. Местные аборигены не компетентно.

а можешь обьяснить - нафига конкретно в этом коде сохранять и восстанавливать регистры? Желательно честко и ясно, а не в стиле архата, который чуть что не так - включает обиженку

Потому как для исполнения кода обработчика прерываний процу нужно где-то хранить данные. Это "где-то" в первую очередь регистры. Но на момент прерывания эти регистры могут использоваться в соответствии с кодом основной программы. Чтоб их не затереть своими данными при вхождении в обработчик их запихивают в стек командой PUSH, а это 2 такта, а по завершению возвращают из стека обратно РОР, тоже 2 такта. Потому в идеале надо чтоб обработчик использовал минимум регистров и не сохранял лишних. Компилятор обычно этими 2-я вопросами не сильно озабочен. Потому и имеем вход+выход в пустое прерывание под 80 тактов. Но Ахат , помница, говорил что ему там че-то удалось сокращать. Если не врал конечно. 

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

 

Green
Offline
Зарегистрирован: 01.10.2015

Проще.) Компилятор сохраняет в стеке, используемые в функции прерывания, регистры.  Если в функции прерывания используются и другие (дополнительные) функции (компилятор не может определить используемые регистры), тогда контекст сохраняется по максимуму, т.е. все регистры! 

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

Green пишет:

 тогда контекст сохраняется по максимуму, т.е. все регистры! 

Ну такое я тоже видел, но в более новых версиях лучше, он сохраняет только нужные. Если б еще и оптимизировал код для сокращения используемых регистров! А че воще значить "компилятор не может определить используемые регистры" - ему все исходники дали, как так "не может"? Он плохо старается... Может речь о линковке с библиотечными функциями? 

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

DEL.

SAB
Offline
Зарегистрирован: 27.12.2016

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

Green
Offline
Зарегистрирован: 01.10.2015

Logik пишет:
А че воще значить "компилятор не может определить используемые регистры" - ему все исходники дали, как так "не может"? Он плохо старается... 


Ленится.) А может, в этой функции, там ещё тыща функций вызывается?) Нет, компилятор не может видеть содержимое вызываемой функции, он видит только прототип, а функция может быть вообще в другом модуле.

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

Продолжу по своей теме. Согласен с тем, что нужно и можно увеличить период вызова прерывания Таймера до 20-30 мкс. Это конечно всё равно мало. Нужно оптимизировать уже написанный код основной программы. В ней тоже происходит какая-либо работа - опрашиваются микровыключатели на размыкание цепи (при размыкания происходит немедленный останов), опрашивается сенсор дисплея. Сенсор понятно, можно опрашивать 10-20 раз в секунду, биты в регистре выставлять вручную и через прерывание, а не функцией analogRead() выявлять факт касания сенсора, но когда идет его опрос, то всё равно этот опрос пересекается с работой датчиков. Микрики через простые опросы пинов опрашиваются быстро. Но мне всё равно хотелось бы, так как и интересно и нужно (придёт и к этому моменту) попробовать написать ассемблерные вставки. Запись в порт МК через ассемблер я понял как

asm volatile("sbi %0, 0x01" :: "I" (_SFR_IO_ADDR(PORTB)))

У меня вопрос по чтению портов, такая инструкция не подходит

asm volatile ("in %0, %1" : "=r" (var) : "I" (_SFR_IO_ADDR(PORTB)));

Чтение-запись Си-шных переменных, регистров МК (например я не знаю как прочитать регистры Таймера TCNT5H, TCNT5L) также не могу прочитать...

asm volatile ("in %0, %1" : "=r" (var_T) : "I" (_SFR_IO_REG_P(TCNT4L)));

Вроде много где почитал, но конкретных примеров нет.

Прошу помощи, ребята, подскажите синтаксис.

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

Green пишет:

Logik пишет:
А че воще значить "компилятор не может определить используемые регистры" - ему все исходники дали, как так "не может"? Он плохо старается... 


Ленится.) А может, в этой функции, там ещё тыща функций вызывается?) Нет, компилятор не может видеть содержимое вызываемой функции, он видит только прототип, а функция может быть вообще в другом модуле.

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

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

AlexBajdin59rus пишет:

Запись в порт МК через ассемблер я понял как

asm volatile("sbi %0, 0x01" :: "I" (_SFR_IO_ADDR(PORTB)))...

Ну так уж зарубатся особого смысла нет, кроме особых случаев*.  PORTB|=0x01  скорей всего и станет "sbi %0, 0x01". 

Я в случаях средней сложности просто дизассамблирую что получилось и смотрю на сколько все плохо.  Для этого в батнике держу что-то типа "c:\Program Files (x86)\Arduino\hardware\tools\avr\bin\avr-objdump.exe"  -d sketch_sep18a.ino.elf >dtmf.asm . Файл *.ino.elf из папки build

Для побитовой работы с портами пользуюсь bitSet, bitClear, bit_is_set, bit_is_clear Обычно все норм при использовании констант.

*Про особый случай. WS2812b. Написал на ассемблерной вставке - работает. Переписал на си, контролируя чтоб получилось похожим как на асм - работает. Поменялись настройки - не работает, поменялась версия ИДЕ - не работает, поменялась фаза луны - не работает. Вытер нах, оставил на ассемблере.

 

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

AlexBajdin59rus пишет:

Прошу помощи, ребята, подскажите синтаксис.

Помогаю: это не нужно делать ассемблерными вставками. Они НЕ ускорят работу программы.

Использовать в таких случаях ассемблер - просто мастурбация, т.е. занятие безусловно приятное, но совершенно бесплодное. Ассемблер применяют в критичных по времени исполнения кусках и те программисты, которые реально могут написать код, более быстрый, чем компилятор. Пример: библиотека fastled, вероятно единственный в Ардуино пример оправданного использования ассемблера.

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

Я же научил тебя получать листинг на асме? Так сам посмотри, что и как делает компилятор.

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

Еще раз и последний, если продолжишь ипать свое безумное прерывание - дальше участваовать не стану. Я написал тебе, что ожидать прекращения импульсов в прерывании - дикий бред. Настаиваешь на своем? Ок, но уже без меня.

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

ЕвгенийП пишет:

Для начала, изучить систему команд процессора для которого пишете

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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

AlexBajdin59rus пишет:

Прошу помощи, ребята, подскажите синтаксис.

Неправда, если бы Вы действительно просили помощи, Вы бы отвечали на вопросы, которые Вам задают.

Я Вас как человека спросил

ЕвгенийП пишет:

AlexBajdin59rus пишет:
но может пока ищу, Вы мне быстрее подскажите как например выставить на порту В пин в высокий уровень...?

Пробую так:

asm volatile( "sbi PORTB, 3" );

не получается...

Какой пин Вы здесь выставляете?

Где ответ?

Также я дал Вам ссылку на руководство по машинным командам. Судя по дальнейшему, оно не прочитано. Так какая помощь Вам нужна, если Вы сами не работаете?

Теперь вот Вы пишете

AlexBajdin59rus пишет:
такая инструкция не подходит

asm volatile ("in %0, %1" : "=r" (var) : "I" (_SFR_IO_ADDR(PORTB)));

Что значит "не проходит"? МК взрывается, гаснет свет во всём доме, или что? Что именно идёт не так?

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

Во-вторых, потрудитесь внятно объяснять что у Вас не работает. У меня, например, такая инструкция "проходит" на ура! Значит, у Вас что-то другое "не проходит" в той части скетча, которую Вы не показали.

AlexBajdin59rus пишет:

Вроде много где почитал, но конкретных примеров нет.

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

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

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

ЕвгенийП пишет:

например, вот этот пост - вполне адекватное изложение с херовой тучей примеров.

Женя! Я уже дал ссылку на этот текст в №47.

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

wdrakula пишет:

Еще раз и последний, если продолжишь ипать свое безумное прерывание - дальше участваовать не стану. 

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

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

Logik пишет:

wdrakula пишет:

Еще раз и последний, если продолжишь ипать свое безумное прерывание - дальше участваовать не стану. 

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

Понял! :-)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

wdrakula пишет:

Женя! Я уже дал ссылку на этот текст в №47.

Судя по реакции ТС на наши ссылки, и в третий раз давать придётся :-)

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

ЕвгенийП пишет:

AlexBajdin59rus пишет:

Прошу помощи, ребята, подскажите синтаксис.

Неправда, если бы Вы действительно просили помощи, Вы бы отвечали на вопросы, которые Вам задают.

Я Вас как человека спросил

ЕвгенийП пишет:

AlexBajdin59rus пишет:
но может пока ищу, Вы мне быстрее подскажите как например выставить на порту В пин в высокий уровень...?

Пробую так:

asm volatile( "sbi PORTB, 3" );

не получается...

Какой пин Вы здесь выставляете?

Где ответ?

Также я дал Вам ссылку на руководство по машинным командам. Судя по дальнейшему, оно не прочитано. Так какая помощь Вам нужна, если Вы сами не работаете?

Теперь вот Вы пишете

AlexBajdin59rus пишет:
такая инструкция не подходит

asm volatile ("in %0, %1" : "=r" (var) : "I" (_SFR_IO_ADDR(PORTB)));

Что значит "не проходит"? МК взрывается, гаснет свет во всём доме, или что? Что именно идёт не так?

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

Во-вторых, потрудитесь внятно объяснять что у Вас не работает. У меня, например, такая инструкция "проходит" на ура! Значит, у Вас что-то другое "не проходит" в той части скетча, которую Вы не показали.

AlexBajdin59rus пишет:

Вроде много где почитал, но конкретных примеров нет.

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

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

Не знаю как вы много раз цитируете...

1) Да, действительно я тогда был не прав, на тот момент не знал синтаксиса этой инструкции. Я тогда хотел выставить 3 бит, а выставлял нулевой и первый.

2) Видимо немного запутался и не то написал. Инструкция проходит, да, а вот дальнейшие действия неправильные. 

  var = 0xF0;
  PORTB = 0x00;
  asm volatile ("in %0, %1" : "=r" (var) : "I" (_SFR_IO_ADDR(PORTB)));
  Serial.println(String(var) + " - var asm");   // выводит 0x00
  var = 0xFF;
 // а вот тут я что-то неправильно делаю
  asm volatile ("out %1, %0" : "=r" (var) : "I" (_SFR_IO_ADDR(PORTB)));
  Serial.println(String(PORTB) + " - PORTB");   //  НЕПРАВИЛЬНО! выводит 0x1B
  // что я делаю не так ??? модификатор нужен другой ???

3) А что, нужно конкретные ссылки приводить...? Набираешь "ассемблерные вставки" в поисковике и они одни и те же у всех вылазят.

4) перечитываю ещё раз ТОТ ПОСТ вроде начинает доходить, стр. 8 этой книги вроде как пример для Си-шных переменных...?

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

wdrakula пишет:

AlexBajdin59rus пишет:

Прошу помощи, ребята, подскажите синтаксис.

Помогаю: это не нужно делать ассемблерными вставками. Они НЕ ускорят работу программы.

Использовать в таких случаях ассемблер - просто мастурбация, т.е. занятие безусловно приятное, но совершенно бесплодное. Ассемблер применяют в критичных по времени исполнения кусках и те программисты, которые реально могут написать код, более быстрый, чем компилятор. Пример: библиотека fastled, вероятно единственный в Ардуино пример оправданного использования ассемблера.

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

Я же научил тебя получать листинг на асме? Так сам посмотри, что и как делает компилятор.

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

Еще раз и последний, если продолжишь ипать свое безумное прерывание - дальше участваовать не стану. Я написал тебе, что ожидать прекращения импульсов в прерывании - дикий бред. Настаиваешь на своем? Ок, но уже без меня.

1) Занятие конечно приятное, помню, давно пробовал писать программу на Асме для PIC-контроллера.

2) Попробовать -то надо... :-)

3) Fasted нашел, буду смотреть (она и ТОЙ ССЫЛКЕ есть)

4) Листинга на Асме нет в ПлатформИО, там только бинарный код... :-( Нужно попробовать через АтмелСтудио посмотреть...

5) Всё таки буду пробовать ожидать окончания импульсов в прерывании - какие-то минимальные сравнения делать там и оттуда получать выводы (или булевую переменную о выявлении сурогата)

Спасибо! Но за помощью я к тебе ещё обращусь ;-)

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

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

Тем более, что судя по:

AlexBajdin59rus пишет:

  var = 0xF0;
  PORTB = 0x00;
  asm volatile ("in %0, %1" : "=r" (var) : "I" (_SFR_IO_ADDR(PORTB)));
  Serial.println(String(var) + " - var asm");   // выводит 0x00
  var = 0xFF;
 // а вот тут я что-то неправильно делаю
  asm volatile ("out %1, %0" : "=r" (var) : "I" (_SFR_IO_ADDR(PORTB)));
  Serial.println(String(PORTB) + " - PORTB");   //  НЕПРАВИЛЬНО! выводит 0x1B
  // что я делаю не так ??? модификатор нужен другой ???

моя просьба

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

проигнорирована.

Ну, тогда разбирайтесь сами. Удачи Вам.

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

Какие-то "обидки" у Вас... Я написал код, прокомментировал что выводит при этом коде, если человек знающий, сразу поймёт в чём дело. Зачем запускать код у себя, если вот он? Я понимаю что тут то-ли адрес, то-ли смещение, то-ли ещё чего (совсем другой регистр из 32-х ассемблерных) передаётся в порт, но как это исправить пока не в моих силах, знаний и опыт ане хватает. Так нет, чтобы подсказать, пишете ерунду...

Давайте писать по существу и не более, зачем хламить мою ветку форума

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

AlexBajdin59rus пишет:

Какие-то "обидки" у Вас...

Какие? Вы, видимо, можете разбираться в программах не запуская их, а я - нет. Не умею я так. Поэтому я предложил Вам работать самому и пожелал удачи. Никаких обидок!

AlexBajdin59rus пишет:

Я написал код, прокомментировал что выводит при этом коде, если человек знающий, сразу поймёт в чём дело. Зачем запускать код у себя, если вот он?

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

Я, видимо, не "человек знающий", а потому, мне для того, чтобы Вам что-то точно подсказать, нужно:

  1. запустить Ваш код;
  2. увидеть проблему самому;
  3. поправить ошибку;
  4. запустить ешё раз;
  5. убедиться, что проблема ушла;
  6. и только тогда Вам подсказывать.

Я всегда так делаю. Если Вас такой подход не устраивает - продолжайте без меня.

AlexBajdin59rus пишет:

Давайте писать по существу и не более, зачем хламить мою ветку форума

Так вроде я с Вами уже попрощался, пожелал Вам удачи и не собирался её более захламлять. Это Вы зачем-то захламляете.

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

ТС Как по вашему происходит вывод переменной в порт или чтение из порта ???

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

Komandir пишет:

ТС Как по вашему происходит вывод переменной в порт или чтение из порта ???

Чтение из порта, как я понял, 

asm volatile ("in %0, %1" : "=r" (var) : "I" (_SFR_IO_ADDR(PORTB)));

а вывод командой

asm volatile ("out %1, %0" : "=r" (Rd) : "I" (P));

Но почему я подставляя VAR

asm volatile ("out %1, %0" : "=r" (var) : "I" (_SFR_IO_ADDR(PORTB)));

не могу отправить значение? Вернее отправляется значение отличное от значения, находящегося в VAR.

 

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

Берем простой код:

uint8_t var=0xF0;
void setup() {
  PORTB=var;
  var=PINB;
}

void loop() {
}

Компилируем и смотрим где лежат результаты компиляции:

...

"D:\\SoftWare\\arduino-1.8.13\\hardware\\tools\\avr/bin/avr-size" -A "C:\\Windows\\TEMP\\arduino_build_841817/i2c-scanner.ino.elf"
Скетч использует 5694 байт (17%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 688 байт (33%) динамической памяти, оставляя 1360 байт для локальных переменных. Максимум: 2048 байт.
 
Идем проводником в папку где лежат результаты компиляции - в моем случае это C:\Windows\TEMP\arduino_build_841817
и открываем файл с листингом.
Там находим то, что породил наш код:
...
uint8_t var=0xF0;
void setup() {
  PORTB=var;
  f2: 80 91 00 01 lds r24, 0x0100 ; 0x800100 <__DATA_REGION_ORIGIN__>
  f6: 85 b9        out 0x05, r24 ; 5
D:\SoftWare\arduino-1.8.13\portable\sketchbook\sketch_jul23a/sketch_jul23a.ino:4
  var=PINB;
  f8: 83 b1        in r24, 0x03 ; 3
  fa: 80 93 00 01 sts 0x0100, r24 ; 0x800100 <__DATA_REGION_ORIGIN__>
D:\SoftWare\arduino-1.8.13\portable\sketchbook\sketch_jul23a/sketch_jul23a.ino:5
}
  fe: 08 95        ret
...
 

 

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

При выводе сначала читается в регистр содержимое ячейки памяти, где лежит var и потом содержимое этого регистра выводится в порт.

При чтении сначала читается значение из порта в регистр и потом содержимое регистра выводится в ячейку памяти, где лежит var.

 

ГДЕ ЭТО У ВАС ???

Как видно из листинга - НИЧЕГО ЛИШНЕГО для данного простого кода компилятор не придумал !

Вам тут что бесплатные курсы inline assembler ? Вы с обычным assembler сначала разберитесь !  А еще лучше для начала понять логику работы процессоров !!!

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

ВО!!! Спасибо! Вот дельная помощь! Сейчас буду разбираться, с этими всеми _SFR_IO8, _MMIO_BYTE, __SFR_OFFSET!

Прямо пальцем ткнули что куда!

 

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

Komandir пишет:

При выводе сначала читается в регистр содержимое ячейки памяти, где лежит var и потом содержимое этого регистра выводится в порт.

При чтении сначала читается значение из порта в регистр и потом содержимое регистра выводится в ячейку памяти, где лежит var.

 

ГДЕ ЭТО У ВАС ???

Как видно из листинга - НИЧЕГО ЛИШНЕГО для данного простого кода компилятор не придумал !

Вам тут что бесплатные курсы inline assembler ? Вы с обычным assembler сначала разберитесь !  А еще лучше для начала понять логику работы процессоров !!!

Да, но у меня же в переменную значение правильно записывается... А про запись значения в порт, я получается правильно предполагал, что не значение из переменной записывается, а что-то другое...

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

AlexBajdin59rus пишет:

Да, но у меня же в переменную значение правильно записывается... А про запись значения в порт, я получается правильно предполагал, что не значение из переменной записывается, а что-то другое...

Уверены что правильно ??? Нельзя ничего записать в переменную без команды записи в память. Попробуйте значение отличное от дефолтного нуля и увидите. Команды IN OUT работают с регистром, а не с памятью !

http://www.gaw.ru/html.cgi/txt/doc/micros/avr/asm/start.htm

Рано Вам еще в assembler !!! В inline assembler я сам вникал долго, хотя обычный assembler я использую 30+ лет ...

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

Учиться никогда не поздно! Раньше я писал на асме для PIC. Знания минимальные остались. Теперь будем учиться большему! Так что больше позитива! :-)

Komandir
Offline
Зарегистрирован: 18.08.2018
Компилятор в одном случае "додумал" правильно: 
 asm volatile ("in %0, %1" : "=r" (var) : "I" (_SFR_IO_ADDR(PORTB)));
 120: 65 b1        in r22, 0x05 ; 5
 122: 60 93 06 01 sts 0x0106, r22 ; 0x800106 <var>
а в другом нет:
 asm volatile ("out %1, %0" : "=r" (var) : "I" (_SFR_IO_ADDR(PORTB)));
 120: 85 b9        out 0x05, r24 ; 5
 122: 80 93 06 01 sts 0x0106, r24 ; 0x800106 <var>

Странные вольности он себе позволил ! Вместо одной команды - вставляет две !

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

Komandir пишет:

Компилятор в одном случае "додумал" правильно: 
 asm volatile ("in %0, %1" : "=r" (var) : "I" (_SFR_IO_ADDR(PORTB)));
 120: 65 b1        in r22, 0x05 ; 5
 122: 60 93 06 01 sts 0x0106, r22 ; 0x800106 <var>
а в другом нет:
 asm volatile ("out %1, %0" : "=r" (var) : "I" (_SFR_IO_ADDR(PORTB)));
 120: 85 b9        out 0x05, r24 ; 5
 122: 80 93 06 01 sts 0x0106, r24 ; 0x800106 <var>

Странные вольности он себе позволил ! Вместо одной команды - вставляет две !

А как "он" знает, что VAR находиться по адресу  0x800106? Это прописывается на этапе компиляции, сборки (не знаю точно правильное определение процесса)? 

И я не понял:

а в другом нет:
 asm volatile ("out %1, %0" : "=r" (var) : "I" (_SFR_IO_ADDR(PORTB)));
 120: 85 b9        out 0x05, r24 ; 5
 122: 80 93 06 01  sts 0x0106, r24 ; 0x800106 <var>

тут мы хотим записать значение из переменной в порт, что делается инструкцией:

 120: 85 b9        out 0x05, r24 ; 5

Почему после неё присутствует инструкция:

 122: 80 93 06 01  sts 0x0106, r24 ; 0x800106 <var>

Или это ваш код, который я не видел?

Я правильно Вас понял, что в этом странность, двух инструкций?

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

Компилятору то не знать где он придумал хранить переменную ???

Это  Ваш код так разворачивается в листинг и во втором случае он не верный !

Вот так должно выглядеть, что бы правильно работало !

чтение PORTB в переменную var:

 asm volatile (
    "in __tmp_reg__,%0"  "\n\t"
    "sts var,%0" "\n\t"
    ::  "I" (_SFR_IO_ADDR(PORTB)));
 

запись переменной var в PORTB:

  asm volatile (
    "lds __tmp_reg__,var  " "\n\t"
    "out %0,__tmp_reg__   "  "\n\t"
    ::  "I" (_SFR_IO_ADDR(PORTB)));

 

 

ua6em
ua6em аватар
Онлайн
Зарегистрирован: 17.08.2016

Komandir пишет:

Вам тут что бесплатные курсы inline assembler ?

а хотелось! Как у Питера Нортона ...

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

Komandir пишет:

Странные вольности он себе позволил ! Вместо одной команды - вставляет две !

;)))))) Если троллишь, то малацца!

Но если и правда не не знаешь, в чем собачка порылась, то беда. :((

Green
Offline
Зарегистрирован: 01.10.2015

ЕвгенийП пишет:

Мужики, ну ведь среда только!


Вот и пятница пришла...
Но тема полезная, всё ж таки.)

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

wdrakula А ну как просвети ... чего это во втором случае он не справился ?

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

Komandir пишет:

wdrakula А ну как просвети ... чего это во втором случае он не справился ?

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

;))))

Там немного логика "черезжопная" в синтаксисе вставок, потому я ТС-у и не советую этим заниматься.

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

Меня другое озадачило - просили одну команду на два опкода - получили две на шесть опкодов.

ИМХО он должен был ругнуться, а не пытаться поправить ситуацию.

В первом блоке выходные, во втором входные ...

asm(code : output operand list : input operand list [: clobber list]);
wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

это работа с переменными, при вставках нужно же загрузить и выгрузить их. Там, чтобы выиграть такты, нужно ну вот-прям очень хорошо знать, что ты делаешь. ;)) Чаще всего это нужно для очень точного "ногодрыга" потому я и написал, что (ИМХО) единственное оправданное применение, которое я видел - библиоетка fastled.

Komandir
Offline
Зарегистрирован: 18.08.2018
Komandir
Offline
Зарегистрирован: 18.08.2018
Для ТС:
вывод в порт работает правильно, если написать так:
asm volatile ("out %1, %0" :: "r" (var), "I" (_SFR_IO_ADDR(PORTB)));
 
 asm volatile ("out %1, %0" :: "r" (var), "I" (_SFR_IO_ADDR(PORTB)));
  f4: 80 91 00 01 lds r24, 0x0100 ; 0x800100 <__DATA_REGION_ORIGIN__>
  f8: 85 b9        out 0x05, r24 ; 5
 
 
 
P.S.

Попробовал немного "поизвращаться" ...

const uint8_t PROGMEM var[]={0xF0};
void setup() {
 asm volatile ("out %1, %0" :: "r" (pgm_read_byte(var)), "I" (_SFR_IO_ADDR(PORTB)));
}
void loop() {
}
 asm volatile ("out %1, %0" :: "r" (pgm_read_byte(var)), "I" (_SFR_IO_ADDR(PORTB)));
  e0: e4 eb        ldi r30, 0xB4 ; 180
  e2: f0 e0        ldi r31, 0x00 ; 0
  e4: e4 91        lpm r30, Z
  e6: e5 b9        out 0x05, r30 ; 5
 
Справляется компилятор и с данными во FLASH.