Расчет угла отклонения джойстика

Тема в разделе "Схемотехника, компоненты, модули", создана пользователем Spell, 20 мар 2016.

  1. Spell

    Spell Нерд

    Добрый день, коллеги.
    Разбираюсь с джойстиком. Хочу посчитать угол (в градусах) отклонения ручки джойстика относительно оси Х.
    Т.к. в ардуине нет функций arcsin (arccos) решил решить задачу в лоб, и получать угол через таблицу синусов.
    Т.е. при отклонении джойстика, считать синус угла через величины отклонений по осям и искать это значение в таблице.
    Ниже код, который я наваял.
    Код (C++):

    #define X  A1
    #define Y  A0
    #define Z  A2

    int mSin[360];
    int prevSin = 0;
    int coef = 10000;

    void setup() {
      //формирование массива синусов
      for(int i=0; i<=359; i++)
      {
        float rad = i * 3.14 / 180;
        mSin[i] = int(sin(rad) * coef); //умножение для подтягивания значений в диапазон int
      }
     
      Serial.begin(9600);
      /*
     //посмотреть на массив
      for(int i=0; i<=359; i++)
      {
        Serial.println((String) "a: " + i + " sin: " +  mSin[i]);
      }
      */

    }

    void loop() {
      // переменные для хранения значений
      // отклонения джойстика по осям X и Y
      int x, y;
     
    // считываем текущее значение джойстика
      x = readJoystick(X);
      y = readJoystick(Y);
      float c2 = sqrt(sq(x) + sq(y));
      int mySin = int(y/c2 * coef); //умножение для подтягивания значений в диапазон int
      /*
      //проверка значения синуса
      if(prevSin != mySin)
      {
        Serial.println((String) "X= " + x + " Y= " + y + " Sin = " + mySin);
        prevSin = mySin;
      }
      */


     
    //Предыдущая версия, которая тоже не работает.
     /*
      int i = -1;
      do
        i++;
      while(mSin[i] != mySin && i < 360);
      */


      int i = 0;
      while(mSin[i] != mySin && i < 360)
      {
        i++;
      }
      if(prevSin != mySin)
      {
        Serial.println((String) "X= " + x + " Y= " + y + " Sin = " + mySin + " ug = " + i);
        prevSin = mySin;
      }

    }

    int readJoystick(int axis)
    {
      int val = analogRead(axis);
      val = map(val, 0, 1023, -100, 100);
     
      if(val <= 10 && val >= -10)
        return 0;
       else
        return val;  
    }
    Собственно, проблема в том, что при поиске угла, переменная i принимает значение значительно больше 360 (1800, например). И я не понимаю, почему это происходит. В условии цикла стоит i<360, т.е. не может она быть больше.
    Подскажите, пожалуйста, что я не так делаю? Может я вообще не тот способ выбрал?
     
  2. Megakoteyka

    Megakoteyka Оракул Модератор

    Переделайте условие. Ваше значение должно попадать в диапазон между предыдущим синусом и текущим или текущим и следующим.
     
  3. Spell

    Spell Нерд

    Спасибо. Частично помогло.
    Я не учел, что могут быть большие расхождения при расчете значения синуса.
    Но все равно пришлось еще ограничивать значения x и y в определенных интервалах, и учитывать эти интервалы в расчетах.
     
  4. Maxmi

    Maxmi Нуб

    А вот у меня тот самый матрёшка-джойстик В нули не возвращается. отклонение на сотню с лишним. Он никак не калибруется случайно?
     
  5. Spell

    Spell Нерд

    Джойстик у меня всего один и не Амперка.
    Судя по видео и фоткам на Амперке сами исполнительные механизмы похожи, разница только в платах.
    У меня джойстик возвращается в ноль без проблем и уверенно. Какого-то большого люфта нуля я не заметил.
    Калибровка ему не нужна.
    Может быть имеет смысл посмотреть код? Может там какой множитель стоит эдак на 10 или 100. Вот Вам и отклонение от нуля. Мало ли.
    Ну и брак элемента тоже возможен. Может там возвратная пружина отвалилась или растянулась.
     
  6. AlexU

    AlexU Гуру

    Очень похоже на то, что в компиляторе avr-gcc присутствует баг. Дело в том, что строки кода:
    Код (C++):
    int i = 0;
    while(mSin[i] != mySin && i < 360)
    {
      i++;
    }
    компилятор оптимизирует до:
    Код (C++):
    int i = 0;
    while(mSin[i] != mySin)
    {
      i++;
    }
    Т.е. он считает, что раз 'int i = 0;', то условие 'i < 360' всегда истина и выкидывает проверку этого условия, не учитывая того обстоятельства, что переменная 'i' меняется в теле цикла.

    Вот теперь интересно -- этот баг только в avr-gcc или во всей линейке GCC?
    И как теперь после этого код сочинять?

    PS: проблему решает
    Код (C++):
    volatile int i = 0;
    while(mSin[i] != mySin && i < 360)
    {
      i++;
    }
     
  7. Spell

    Spell Нерд

    Интересный момент.
    А как Вы посмотрели, что он скомпилировал? Декомпилировали двоичный код? Собственно, декомпилировать можно до уровня ассемблера (не слишком сложно), а вот как до высокого уровня?

    Интересно, а без volatile обойтись получится?
    Например, объявить переменную в начале процедуры и потом присвоить ей 0?
     
  8. AlexU

    AlexU Гуру

    До уровня ассемблера, до "высокого" ни как. Потом "домозговал".

    Надо смотреть...
     
  9. Megakoteyka

    Megakoteyka Оракул Модератор

    Вот тут говорят:
    У вас в опциях компилятора -mint8 не фигурирует?
     
  10. AlexU

    AlexU Гуру

    Не фигурирует.
    Не очень понимаю как эта опция (определяющая размерность целых) влияет на то, что компилятор генерирует код, который не проверяет условие 'i < 360'?
     
  11. Megakoteyka

    Megakoteyka Оракул Модератор

    Если тип int будет однобайтным, то переменная всегда будет меньше 360.
     
  12. AlexU

    AlexU Гуру

    Дело не в однобайтовости, а в том, что компилятор создаёт код, в котором отсутствует проверка условия.

    PS: вроде пытался ясно выразить мысль, которая заключается не в том, что условие не выполняется, а в том, что оно вообще не проверяется.