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

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

Всем привет! У меня вопрос по быстродействию выполнения кода. 

У меня очень быстро должно сработать прерывание Таймера, в нем присутствует такая команда: PORTB |= (1 << SOLENOID_DEFECT) SOLENOID_DEFECT определён как: #define SOLENOID_DEFECT 3 // соленоид дозатора Были мысли такие: (1 << SOLENOID_DEFECT) состоит, по моим предположениям, из НЕ одной команды (точно не знаю сколько), берём 0х01 и сдвигаем на 3 бита влево. То есть тратится время на сдвиг. Теперь идея такая, что, чтобы не тратиться на сдвиг, берём число 0х08 и проделываем следуещее: PORTB |= 0х08;




[9:33]
Но почему-то в последнем варианте команда получается на один такт больше...




[9:33]
Замерял по счётчику Таймера,




[9:34]
Я правильно рассуждал? Но ассемблер или компилятор решил по своему или работа его/их состоит по другому, не как я думал?

 

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

Перепишу по другому, скопировал из другого форума.

У меня очень быстро должно сработать прерывание Таймера, в нем присутствует такая команда:

PORTB |= (1 << SOLENOID_DEFECT)

 SOLENOID_DEFECT определён как:

#define SOLENOID_DEFECT 3 // соленоид дозатора

Были мысли такие:

(1 << SOLENOID_DEFECT) состоит, по моим предположениям, из НЕ одной команды (точно не знаю сколько), берём 0х01 и сдвигаем на 3 бита влево. То есть тратится время на сдвиг.

Теперь идея такая, что, чтобы не тратиться на сдвиг, берём число 0х08 и проделываем следующее:

PORTB |= 0х08;

Но почему-то в последнем варианте команда получается на один такт больше... Замерял по счётчику Таймера. Я правильно рассуждал? Но ассемблер или компилятор решил по своему или работа его/их состоит по другому, не как я думал?

 

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

Умнее компилятора быть не получится. Там наверняка используется одна команда SBI.

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

Где именно, в первом или втором варианте? И если можно, то где по Вашему устанавливается бит, в каком месте, ну то есть своими словами описать если

 

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

Первый и второй вариант одно и тоже. Перед трансляцией (!) 1<<3 превращается в 8.
Приведите минимальный код пригодный для трансляции, тогда я покажу конкретнее.

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

С этим согласен. Но у меня мысли примерно такие.

Берём вариант со сдвигом. Я думаю ассемблерные команды там такие, что, в регистр записываем число 0х01, потом применяем команду сдвига на 3 влево. То есть уже 2 команды.

Теперь берём вариант без сдвига (PORTИ |= 0x08). Тут, мне кажется, должно быть на одну команду меньше. Берём уже готовое число 0х08 и производим манипуляцию с портом PORTB (PORTB |= ... )

Я не прав?

Код такой для сравнения:

PORTB |= (1 << 3);
и
PORTB |= 0x08;
 

 

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

Не прав. См. выше.

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

То есть, при трансляци, (она же компиляция, если я правильно понимаю) операция сдвига 1 << 3 заменяется уже готовым числом 0х08? Компилятор то есть по своему код вставляет?

 

sadman41
Онлайн
Зарегистрирован: 19.10.2016
b707
Offline
Зарегистрирован: 26.05.2017

AlexBajdin59rus пишет:

Компилятор то есть по своему код вставляет?


оптимизатор может очень сильно менять код, вплоть до того что просто выкинуть две трети вашего скетча, если посчитает его ненужной. А уж такие простые вещи, как заменить в коде операцию с константами типа 2*2 на заранее вычисленный результат 4 - это для него семечки.
И скорее всего ваши "измерения" о разнице в один такт - тоже результат оптимизации, а не реальная разница между операторами.

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

А листинг глянуть и/или нам показать - религия не позволяет ???

Реакция на прерывание у AVR и так не самая быстрая, так что если скорость реакции критична - надо обязательно смотреть что там компилятор нагородил !

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Чё то ржу...
Это что за соленоид такой, что для него нужно такты считать ?

b707
Offline
Зарегистрирован: 26.05.2017

Kakmyc пишет:
Это что за соленоид такой, что для него нужно такты считать ?

какой-какой - дефектный...

AlexBajdin59rus пишет:

#define SOLENOID_DEFECT 3 // соленоид дозатора

 

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

Komandir пишет:

А листинг глянуть и/или нам показать - религия не позволяет ???

Реакция на прерывание у AVR и так не самая быстрая, так что если скорость реакции критична - надо обязательно смотреть что там компилятор нагородил !

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

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

Kakmyc пишет:
Чё то ржу... Это что за соленоид такой, что для него нужно такты считать ?

Тут не для соленоида такты считаются. Прерывание вызывается каждые 10 мкс, проверяется монета на подлинность, то есть через 160 тактов в Ардуине Мега. При этом у меня должно отображаться что-то на дисплее (подсчитанные монеты). И если в обработчике прерывания выявился суррогат монеты, срабатывает соленоид отбраковки вышенаписанной команды.

 

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

AlexBajdin59rus пишет:

Прерывание вызывается каждые 10 мкс, проверяется монета на подлинность,

Родное сердце. Ты точно МИКРО секунды и МИЛЛИ секундами не перепутал? ;))))

За 10 мкс звук в воздухе пройдет 3.4 мм. ;)) Монета, в монетоприемнике у тебя со скоростью звука движется? Фирменная вещь!!!

(Пипец, какой-то!)

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

AlexBajdin59rus пишет:

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

Так покажи нам тогда. Мы то на ассемблере собаку съели ...

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

Нет, не перепутал. Монета проверяется индуктивным датчиком. На катушку подается импульс, затем подсчитываются затухающие колебания через компаратор (он отдельный слава богу). И вот через каждые 10 мкс смотрим, сколько импульсов компаратора пришло на счётный пин Таймера (счётчик таймера отдельного инкрементруется через пин МК)

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

Komandir пишет:

AlexBajdin59rus пишет:

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

Так покажи нам тогда. Мы то на ассемблере собаку съели ...

Примерный код обработчика такой, ещё не до конца написанный...

ISR(TIMER5_COMPA_vect)
{// сохраним число пришедших импульсов с компаратора
  CNT_TIMER_COMP = TCNT_COMP;
  if (BUF_TIMER_COMP != CNT_TIMER_COMP)
    {// если импульсы продолжают приходить
      TCNT_ChecCompImp = 0x00;    // начнем счёт сначала
      OCRxA_ChecCompImp = 160;    // 10 мкС (отлавливать окончание импульсов компаратора)
      BUF_TIMER_COMP = CNT_TIMER_COMP;
    }
  else 
    {// счёт импульсов закончился
      switch (HighLowFreq)
        { case 1: // проверим на подлиность на ВЫСОКОЙ ЧАСТОТЕ
                  switch (G_Param.levelVerifi)
                    { case Global_Param::LevelVerifi::MaxVerifi:
                                    // если уровень проверки подлинности максимальный
                                    if (CNT_TIMER_COMP != G_Param.HighFreqImpCompMAX)
                                      { SOLENOID_DEFECT_ON;
                                        STOP_MOTOR;
                                        CoinOK = false; 
                                      }
                                    break;

                      default:      // если уровень проверки подлинности отличный от максимального
                                    if ((CNT_TIMER_COMP < G_Param.CNT_HighFreqImpCompMin) ||
                                        (CNT_TIMER_COMP > G_Param.CNT_HighFreqImpCompMax))
                                      { SOLENOID_DEFECT_ON;
                                        STOP_MOTOR;
                                        CoinOK = false; 
                                      }
                                    break;
                    }
                  
                  if (CoinOK)
                    {// переход к низкой частоте
                      HighLowFreq = 0;         // установка флага на период низкой частоты
                      PIN_LOW_FREQ_1;          // откроем транзистор низкой частоты
                      PIN_HIGH_FREQ_1;         // откроем транзистор накачки
                    }
                  
                  break;

          case 0: // проверим на подлиность на НИЗКОЙ ЧАСТОТЕ
                  switch (G_Param.levelVerifi)
                    { case Global_Param::LevelVerifi::MaxVerifi:
                                    // если уровень проверки подлинности максимальный
                                    if (CNT_TIMER_COMP != G_Param.LowFreqImpCompMAX)
                                      { SOLENOID_DEFECT_ON;
                                        STOP_MOTOR;
                                        CoinOK = false; 
                                      }
                                    break;

                      default:      // если уровень проверки подлинности отличный от максимального
                                    if ((CNT_TIMER_COMP < G_Param.CNT_HighFreqImpCompMin) ||
                                        (CNT_TIMER_COMP > G_Param.CNT_HighFreqImpCompMax))
                                      { SOLENOID_DEFECT_ON;
                                        STOP_MOTOR;
                                        CoinOK = false; 
                                      }
                                    break;
                    }
                  
                  if (CoinOK)
                    {// переход к высокой частоте
                      HighLowFreq = true;               // установка флага на период высокой частоты
                      PIN_LOW_FREQ_0;                   // закроем транзистор низкой частоты
                      PIN_HIGH_FREQ_1;                  // откроем транзистор накачки
                    }
                  
                  break;
        }

      
      CNT_TIMER_COMP = 0;                       // обнулим счётчик таймер 
      TCCRxB_MainImp |= (1 << CSx0);            // запустим таймер на 2 мкС
    }
  
  TCNT_ChecCompImp = 0x00;                      // начнем счёт таймера сначала
}

 

Kakmyc
Offline
Зарегистрирован: 15.01.2018

И что ?
После этого монета испаряется и на ее месте тут же материализуется другая ?

В общем или система неправильно спроектирована или с башкой у кого то проблемы и этот кто то не может сформулировать что же ему нужно

b707
Offline
Зарегистрирован: 26.05.2017

AlexBajdin59rus пишет:

Примерный код обработчика такой, ещё не до конца написанный...

ISR(TIMER5_COMPA_vect)
{// сохраним число пришедших импульсов с компаратора
  CNT_TIMER_COMP = TCNT_COMP;
  if (BUF_TIMER_COMP != CNT_TIMER_COMP)
    {// если импульсы продолжают приходить
      TCNT_ChecCompImp = 0x00;    // начнем счёт сначала
      OCRxA_ChecCompImp = 160;    // 10 мкС (отлавливать окончание импульсов компаратора)
      BUF_TIMER_COMP = CNT_TIMER_COMP;
    }
  else 
    {// счёт импульсов закончился
      switch (HighLowFreq)
        { case 1: // проверим на подлиность на ВЫСОКОЙ ЧАСТОТЕ
                  switch (G_Param.levelVerifi)
                    { case Global_Param::LevelVerifi::MaxVerifi:
                                    // если уровень проверки подлинности максимальный
                                    if (CNT_TIMER_COMP != G_Param.HighFreqImpCompMAX)
                                      { SOLENOID_DEFECT_ON;
                                        STOP_MOTOR;
                                        CoinOK = false; 
                                      }
                                    break;

                      default:      // если уровень проверки подлинности отличный от максимального
                                    if ((CNT_TIMER_COMP < G_Param.CNT_HighFreqImpCompMin) ||
                                        (CNT_TIMER_COMP > G_Param.CNT_HighFreqImpCompMax))
                                      { SOLENOID_DEFECT_ON;
                                        STOP_MOTOR;
                                        CoinOK = false; 
                                      }
                                    break;
                    }
                  
                  if (CoinOK)
                    {// переход к низкой частоте
                      HighLowFreq = 0;         // установка флага на период низкой частоты
                      PIN_LOW_FREQ_1;          // откроем транзистор низкой частоты
                      PIN_HIGH_FREQ_1;         // откроем транзистор накачки
                    }
                  
                  break;

          case 0: // проверим на подлиность на НИЗКОЙ ЧАСТОТЕ
                  switch (G_Param.levelVerifi)
                    { case Global_Param::LevelVerifi::MaxVerifi:
                                    // если уровень проверки подлинности максимальный
                                    if (CNT_TIMER_COMP != G_Param.LowFreqImpCompMAX)
                                      { SOLENOID_DEFECT_ON;
                                        STOP_MOTOR;
                                        CoinOK = false; 
                                      }
                                    break;

                      default:      // если уровень проверки подлинности отличный от максимального
                                    if ((CNT_TIMER_COMP < G_Param.CNT_HighFreqImpCompMin) ||
                                        (CNT_TIMER_COMP > G_Param.CNT_HighFreqImpCompMax))
                                      { SOLENOID_DEFECT_ON;
                                        STOP_MOTOR;
                                        CoinOK = false; 
                                      }
                                    break;
                    }
                  
                  if (CoinOK)
                    {// переход к высокой частоте
                      HighLowFreq = true;               // установка флага на период высокой частоты
                      PIN_LOW_FREQ_0;                   // закроем транзистор низкой частоты
                      PIN_HIGH_FREQ_1;                  // откроем транзистор накачки
                    }
                  
                  break;
        }

      
      CNT_TIMER_COMP = 0;                       // обнулим счётчик таймер 
      TCCRxB_MainImp |= (1 << CSx0);            // запустим таймер на 2 мкС
    }
  
  TCNT_ChecCompImp = 0x00;                      // начнем счёт таймера сначала
}

это не обработчик, а полный бред. И он никак не может вызываться каждые 20мкс, не обманывайте себя. У вас прерывание на прерывание налезает.

Обработчик прерывания должен быть максимально коротким, буквально пара строк кода. В вашем случае в нем должен быть только счетчик импульсов и НИЧЕГО БОЛЬШЕ,

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

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

Кстати, операторы SOLENOID_DEFECT_ON;   STOP_MOTOR; - это что? макросы? - покажите их код

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

Это вот этот обработчик ты планируешь вызывать каждые 10 мкс???

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

b707
Offline
Зарегистрирован: 26.05.2017

wdrakula пишет:

У нас, вероятно, квалификации не хватит, чтобы тебе помочь.

сарказм? - хотя нет, в этом случае человеку помочь действительно сложно :)

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

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

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

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

Операторы SOLENOID_DEFECT_ON;   STOP_MOTOR; это и есть вопрос с самого начала:



#define SOLENOID_DEFECT_ON   PORT_WORK |=  (1 << SOLENOID_DEFECT)

 

b707
Offline
Зарегистрирован: 26.05.2017

AlexBajdin59rus пишет:

Операторы SOLENOID_DEFECT_ON;   STOP_MOTOR; это и есть вопрос с самого начала:



#define SOLENOID_DEFECT_ON   PORT_WORK |=  (1 << SOLENOID_DEFECT)

 

в таком обработчике разница между этими операторами (даже если она и есть) - не имеет ровно никакого значения. Этот код занимает СОТНИ и ТЫСЯЧИ тактов, а вы задались вопросом, как сэкономить один такт? - займитесь лучше переносом обработки в программу

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

На осцилограмме затухающих колебаний там как раз эти колебания в этот диапазон временных рамок и влезает. Программа была написана давно на ассемблере. Мне дали задание внедрить сенсорный экран, вот я и стараюсь. Но пишу на Си++, а кода ассемблерного этой машины у меня нет, поэтому придумываю всё сам с "нуля". Так что сильно не критикуйте, а давайте советы :-) (хотя к любой критике готов)

 

 

b707
Offline
Зарегистрирован: 26.05.2017

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

rkit
Offline
Зарегистрирован: 23.11.2016

Никаких тут тысяч тактов нет, обычное прерывание.

b707
Offline
Зарегистрирован: 26.05.2017

rkit пишет:

Никаких тут тысяч тактов нет, обычное прерывание.

пока считает (выполняется ветка if) - обычное прерывание, а как закончит  (ветка else, строка 9) - так дальше бред

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

b707 пишет:

AlexBajdin59rus пишет:

Операторы SOLENOID_DEFECT_ON;   STOP_MOTOR; это и есть вопрос с самого начала:



#define SOLENOID_DEFECT_ON   PORT_WORK |=  (1 << SOLENOID_DEFECT)

 

в таком обработчике разница между этими операторами (даже если она и есть) - не имеет ровно никакого значения. Этот код занимает СОТНИ и ТЫСЯЧИ тактов, а вы задались вопросом, как сэкономить один такт? - займитесь лучше переносом обработки в программу

Не соглашусь с Вами, с приведенными числами "СОТНИ и ТЫСЯЧИ тактов". У меня же тут проверками-ветвлениями, switch-case-ми. Работает максимально быстро. Если приглядеться, то в самом начале, если число импульсов компаратора разнятся с буфером, тут же выходим из прерывания. Если равны (импцльсы закончились), через switch-case (что быстрее чем if-else) число импульсов сравнивается с эталоном и далее реакция на 3 команды. Ну и переключение на частоту высокую или низкую (противоположную)

 

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Я бы сделал так:
Подаём импульс на индуктивный датчик запускаем таймер, врубаем прерывание по INT0/INT1.
В нем считаем импульсы, обнуляем таймер.
Как только импульсы кончились и таймер не обнулился, делаем выводы , основываясь на результатах счётчика.
Это цикл проверки.
Только он должен быть быстрым, в нем два действия всего. При выполнении проверки все остальные прерывания должны быть выключены.
В цикле в это время должно крутится только что то типа этого:

switch (mode){
case check:
if(millis()-timer>=interval){
mode=nextStep;
}
}

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

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

Изменил немного начало, убрал пару команд:
 

ISR(TIMER5_COMPA_vect)
{// сравним число пришедших импульсов с компаратора
  if (BUF_TIMER_COMP != TCNT_COMP)
    {// если импульсы продолжают приходить
      OCRxA_ChecCompImp = 160;    // 10 мкС (отлавливать окончание импульсов компаратора)
      BUF_TIMER_COMP = TCNT_COMP;
    }

 

b707
Offline
Зарегистрирован: 26.05.2017

AlexBajdin59rus пишет:

через switch-case (что быстрее чем if-else)

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

насчет длительности прерывания больше спорить не буду.

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

Kakmyc пишет:
Я бы сделал так: Подаём импульс на индуктивный датчик запускаем таймер, врубаем прерывание по INT0/INT1. В нем считаем импульсы, обнуляем таймер. Как только импульсы кончились и таймер не обнулился, делаем выводы , основываясь на результатах счётчика. Это цикл проверки. Только он должен быть быстрым, в нем два действия всего. При выполнении проверки все остальные прерывания должны быть выключены. В цикле в это время должно крутится только что то типа этого: switch (mode){ case check: if(millis()-timer>=interval){ mode=nextStep; } } Теперь у нас идёт процесс замены монеты и всего остального, в том числе и отрисовка дисплея

Эта логика сразу непригодна. Если я правильно понял, что "врубаем прерывание по INT0/INT1", то это подсчёт импульсов с компаратора. Эти импульсы приходят очень быстро - только на них всё и повиснет. А так, по моей задумке, я через 10 мкс сравниваю число этих импульсов с буфером и, если они разнятся, то импульсы продолжают поступать, а если они равны с  числом в буфере, то импульсы закончились. 
Далее, потом, когда код будет боле-менее рабочим, мне сказали сделать всё на СТМ, что тогда исключает Ардуиновские команды millis() и analogRead(), что я и переписываю на свой лад (милис через таймер, аналогРэад вручную настраиваю биты в регистрах АЦП)

b707
Offline
Зарегистрирован: 26.05.2017

AlexBajdin59rus пишет:

мне сказали сделать всё на СТМ, что тогда исключает Ардуиновские команды millis() и analogRead(), что я и переписываю на свой лад (милис через таймер, аналогРэад вручную настраиваю биты в регистрах АЦП)

в СТМ тоже есть и миллис и analogRead()

Есть ардуино для СТМ32, все команды поддерживаются. А критичные места вы можете напрямую через регистры и таймеры написать

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Какая частота колебаний ?

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

Запустите код в Proteus - там есть счетчик тактов и посмотрите сколько тактов от входа до выхода.

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

b707 пишет:

насчет длительности прерывания больше спорить не буду.

Зря. Это прерывание примерно на 10 мкс. Вход-выход уже около 80 тактов и столько же (минимум) внутри. Каждый такт 1/16 мкс ;)))

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

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

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

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

wdrakula пишет:

b707 пишет:

насчет длительности прерывания больше спорить не буду.

Я же написал - вне моей квалификации. 

Да, вне твоей квалификации. Сколько раз ты собираешься это повторить? Ищи тему, где по силам.

Прерывание написано достаточно коротко, потому как при каждом прохождении срабатывает одна из веток. А их общее число на дело не влияет.

AlexBajdin59rus, оба варианта должны давать одинаковый код. Но это все на совести компилятора и его настроек оптимизации. Напрямую писать SBI однозначней. Но основная проблема тут не в этом такте, а в сохранении и восстановлении регистров при входе выходе в прерывание. Местные аборигены не компетентно. Иди на сайт Гувера, ищи Архата, тот когда то говорил что разобрался с этим. Привет передавай.

 

 

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

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

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

Logik пишет:

Напрямую писать SBI однозначней. Но основная проблема тут не в этом такте, а в сохранении и восстановлении регистров при входе выходе в прерывание. Местные аборигены не компетентно. Иди на сайт Гувера, ищи Архата, тот когда то говорил что разобрался с этим. Привет передавай.

Спасибо за совет, схожу туда, как найду Архата, привет передам!
Начал искать, но может пока ищу, Вы мне быстрее подскажите как например выставить на порту В пин в высокий уровень...?

Пробую так:

asm volatile( "sbi PORTB, 3" );

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

 

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

SAB пишет:

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

Давайте не будем писать такие сообщения. Что делаю, то делаю. АСМ (автомат счёта монет) изготавливают у нас, спроектировали его 20 лет назад. Там отображение данных цифрами на семисегментных индикаторах - режимы работы, сколько монет подсчитано, уровни проверок подлинности, настройки. Всё это отображается точками, черточками, числами. Разработали в своё время на ассемблере. Мне дали задание внедрить сенсорный экран, чтобы, сами понимаете, можно было словами отображать все данные АСМ. Кода асм нет, поэтому пишу на Си++ и всё пишу с нуля.

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

Ок. Так получилось, что я немножко понимаю именно в низкоуровневом программировании АВР контроллеров.
Напиши следующее:
1. С какой частотой приходят импульсы в обоих режимах.
2. Сколько их всего должно прийти.
3. Сколько времени монета находится в зоне теста.
----------
Пока хватит. Я примерно понял, что ты делаешь, но хочется понять точнее. Сейчас я ещё не полностью проснулся ;))). В Москве 7-30. Встану часов в 11 и подумаю.

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

wdrakula пишет:
Ок. Так получилось, что я немножко понимаю именно в низкоуровневом программировании АВР контроллеров. Напиши следующее: 1. С какой частотой приходят импульсы в обоих режимах. 2. Сколько их всего должно прийти. 3. Сколько времени монета находится в зоне теста. ---------- Пока хватит. Я примерно понял, что ты делаешь, но хочется понять точнее. Сейчас я ещё не полностью проснулся ;))). В Москве 7-30. Встану часов в 11 и подумаю.

У меня скудная информация на твои вопросы.

1) Частота около 150-200 кГц. Период колебаний разнится, так как состав материала датчика имеет погрешность. Смотрел на осцилограмме, на низкой частоте период около 7 мкс, на высокой около 5 мкс.

2) Колебания с датчика отсекаются компаратором на уровне 0,2В. Компаратор выдаёт также разное количество импульсов, погрешность датчика и разный состав сплава монеты, в среднем около 10. Это когда монета в зоне датчика, когда её нет, число импульсов больше.

3) номинал 1 копейка, (хоть они и вышли из оборота, но в АСМ они предусмотрены), что минимум по диаметру, таких монет пролетают около 20 штук в секунду. 

 

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

Вопросик: есть переменная, как мне присвоить ей значение 0х00 через ассемблерную вставку?

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

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

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

Я пока не понял, задача написать для AVR или STM?

Если для AVR, я бы посоветовал установить AVR STUDIO 7. В ней можно отлаживать полученный код и можно увидеть полученный ассемблерный код. И если вы там найдете не оптимальный (по вашему) код на ассемблере, тогда и думайте, как его улучшить.

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

ОК.

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

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 был верен.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

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

Т.е. может и быстрее отработать пачки if, может медленнее, но программист, работающий на уровне "экономии тактов", скоростью отработки switch не управляет,  

rkit
Offline
Зарегистрирован: 23.11.2016

Медленнее не может

b707
Offline
Зарегистрирован: 26.05.2017

Logik пишет:

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

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