Arduino Mega 2560 Расширение SRAM

Тема в разделе "Arduino & Shields", создана пользователем un1x, 18 окт 2017.

  1. un1x

    un1x Нерд

    Доброго времени суток.
    Возник вопрос по поводу расширения SRAM.
    Организую большую выборку 10000 значений, тип float, считываются с АЦП непрерывно.
    Нужно сохранять эти данные в массиве для последующей обработки и записи на флэш-накопитель в текстовый файл. Возможности разделить на части данные нет, так как сигнал(значение напряжения) нужно считывать непрерывно.
    Гуглил расширение SRAM вроде как на MEGA2560 это возможно. Может быть кто то сталкивался и поделится ссылками на модули и/или решения по данной теме.
     
  2. rkit

    rkit Гуру

    С ацп не считываются флоаты, а uint16, и памяти в меге на них хватает с хорошим запасом. 160к из 256к
     
    Последнее редактирование: 18 окт 2017
  3. un1x

    un1x Нерд

    Я неправильно выразился, при преобразовании(мат. операциях) я должен получить массив в float. Точность до 7го знака после запятой, знаю что это бред но нужно именно так. Далее необходимо прогнать данный массив через цифровой фильтр, и откуда такие данные 160 и 256к? Это вы про flash? Если про неё то как я в неё передам полученный массив? Progmem? Почитав форумы я понял что в flash можно только статик данные передавать, или я ошибаюсь?
     
  4. rkit

    rkit Гуру

    С 256к я накосячил, действительно.
    Вы уверены, что вам нужно всю память выделять, и алгоритм нельзя использовать в инкрементальном режиме? Программируете хорошо?
     
  5. un1x

    un1x Нерд

    Не очень, уровень хомячка. Но если есть толковые доки, то осилить вполне смогу.
     
  6. rkit

    rkit Гуру

    Ну я советую вам оптимизировать работу алгоритма, чтобы он обрабатывал данные по мере получения. Я на 99% уверен, что это возможно. А если невозможно, то не страдайте ерундой, а берите сразу микроконтроллер с достаточным объемом памяти. Это будет гораздо проще, чем присобачивать внешнюю ram-память к меге.
     
  7. DIYMan

    DIYMan Guest

    Вопрос: а что, обязательно сразу хранить массив во float, или допустимо перед прогоном через фильтр пересчитать каждое значение? ;)

    Это я к чему - можно хранить целочисленные данные, а уже потом, при первой необходимости - пересчитывать их во float, если это допустимо. И память сэкономите, кстати ;)

    И ещё: очень часто можно обойтись БЕЗ float, а перевести всё на целочисленную арифметику. С точки зрения арифметики значениями 1.2345678 и 12345678 можно оперировать, исходя из принципа "те же яйца, только перед выдачей конечного результата преобразовать его в нужный вид". На личном примере: показания с датчиков я никогда во float не храню - незачем: вместо этого храню отдельно целую часть, отдельно - дробную, обе в виде целых чисел ;)
     
  8. un1x

    un1x Нерд

    Будьте добры укажите пути оптимизации, где например можно почитать про инкрементальный режим и т.п.
     
  9. rkit

    rkit Гуру

    Эмм, ну в учебнике по алгоримтам. Кнут, искусство программирования - фундаментальная книжка. Ну на самом деле надо просто посмотреть на код и как следует подумать. Этого ни один учебник не заменит.
     
    un1x нравится это.
  10. un1x

    un1x Нерд

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

    DIYMan Guest

    Это зависит от работы вашего алгоритма. Вы писали:
    Давайте рассмотрим на примере: у вас есть N показаний, как делаете вы (условно, конечно):
    Код (C++):

    float data[1000]; // массив данных

    // чтение
    for(int i=0;i<1000;i++)
    {
      data[i] = analogRead(A0)/5.0; // как пример преобразования, конечно
    }

    // фильтр
    for(int i=0;i<1000;i++)
    {
       if((int)data[i] % 2)
         data[i] -= .5;
    }

     
    При этом вы сразу храните кучу float, что в ДАННОМ КОНКРЕТНОМ случае - совершенно не нужно, тот же самый пример можно переписать и с целыми числами ;):

    Код (C++):

    int data[1000]; // массив данных

    // чтение
    for(int i=0;i<1000;i++)
    {
      data[i] = analogRead(A0); // как пример преобразования, конечно
    }

    // фильтр
    for(int i=0;i<1000;i++)
    {
      float val = data[i]/5.0;
       if(data[i] % 2)
       {
         val -= .5;
         data[i] = (int) val;
       }
    }

     

    Тогда давайте сюда реализацию своего фильтра, посмотрим - можно ли там обойтись преобразованиями "на ходу", так сказать. И да - рекурсии стоит избегать всегда, если это возможно ;)
     
  12. un1x

    un1x Нерд

    В данный момент не за пк, как буду скину :)
     
  13. un1x

    un1x Нерд


    Код (C++):

    //---Коэффициенты фильтра------
    int B_0[3]={1,1,1};
    int B_1[3]={0,0,0};
    int B_2[3]={-1,-1,-1};
    float A_1[3]={-1.85733901, -1.84709149, -1.84506849};
    float A_2[3]={0.992295372, 0.992026524, 0.984382369};
    //---Получаем данные------
    for (int i=0; i < col; i++)
    {
       sigIn[i] = analogRead(pinA1)/204.6-2.5;
    }
    //---Фильтрация------
    const int col = 10000;
    float sigOut[10000]={};
       sigOut[0]=sigIn[0];
       sigOut[1]=sigIn[1];
       for (int i=2; i < col; i++){
          for (int z=2; z < 3; z++)
          {
             sigOut[i]=-A_1[z]*sigOut[i-1]-A_2[z]*sigOut[i-2]+B_0[z]*sigIn[i]+B_1[z]*sigIn[i-1]+B_2[z]*sigIn[i-2];
          }
       }
     
     
    Последнее редактирование: 19 окт 2017
  14. DIYMan

    DIYMan Guest

    sigIn - объявлена как массив int:
    Код (C++):
    for (int i=0; i < col; i++)
    //---Коэффициенты фильтра------
    int B_0[3]={1,1,1};
    int B_1[3]={0,0,0};
    int B_2[3]={-1,-1,-1};

    float A_1[3]={-1.85733901, -1.84709149, -1.84506849};
    float A_2[3]={0.992295372, 0.992026524, 0.984382369};

    //---Получаем данные------
    {
       // отображаем диапазон 0-1023 на диапазон 0-32767
       sigIn[i] = map(analogRead(pinA1),0,1023,0,32767);///204.6-2.5;
    }
    //---Фильтрация------
    const int col = 10000;
    /*float*/ int sigOut[10000]={0};

       sigOut[0]=sigIn[0];
       sigOut[1]=sigIn[1];
     
       for (int i=2; i < col; i++){
          for (int z=2; z < 3; z++)
          {
             // вот здесь у нас задача - правильно преобразовать полученное с входа число 0-1023 при помощи фильтров,
             // чтобы на выходе получилось число, которое соответствует числу с плавающей точкой, если к нему применить формулу
             // value / 204.6 - 2.5
           
             float sigOutI_1 = sigOut[i-1]/204.6-2.5;
             float sigOutI_2 = sigOut[i-2]/204.6-2.5;
             float sigIn_I = sigIn[i]/204.6-2.5;
             float sigIn_I_1 = sigIn[i-1]/204.6-2.5;
             float sigIn_I_2 = sigIn[i-2]/204.6-2.5;
           
             float computed = -A_1[z]*sigOutI_1 - A_2[z]*sigOutI_2 + B_0[z]*sigIn_I + B_1[z]*sigIn_I_1 + B_2[z]*sigIn_I_2;
           
             // теперь имеем значение computed. Очевидно, что оно принадлежит к какому-то диапазону.
             // задача - отразить его значение на диапазон 0-32767.
             int result = map(computed, .., .., 0, 32767);
           
             sigOut[i] = result;
          }
       }
     
    Если значения float не могут быть отрицательными - то можно расширить диапазон соответствия, применив вместо int - unsigned int, тогда верхняя граница соответствия будет не 32767, а 65535, что даст нам в два раза больше значений на шкале. Не уверен, что пример подхода даст вам необходимую точность, но принцип - понятен, думаю: мы храним всё в int (т.е. в двух байтах), перед вычислениями преобразовываем во float, результат вычислений - обратно в int, отражением на расширенный диапазон возможных значений, получаемых из порта при помощи analogRead. Насколько такой подход применим в вашем случае - вопрос.
     
    ИгорьК нравится это.
  15. un1x

    un1x Нерд

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

    DIYMan Guest

    Смотрите - вы упомянули про точность до 7 знаков после запятой. Я навскидку написал вариант, когда считаем во float только по мере необходимости, затем храним этот результат как отражение на диапазон -32767 - 32767. Если такая точность приближения (потери всё равно будут, как ни крути) устроит при построении графика - значит, вопрос решён. Если считаете, что вам ну вот прям совсем обязательны эти семь знаков после запятой - надо думать.

    Что бы я предложил: попробовать и ваш подход, и мой вариант - на одном и том же входящем массиве данных с порта. Построить графики, и посмотреть, насколько они похожи.
     
    un1x нравится это.
  17. DIYMan

    DIYMan Guest

    Есть ещё вариант: избавиться от операций с плавающей точкой вовсе: все коэффициенты перевести в long (навскидку вроде вмещаются), результат, полученный с помощью фильтра - отображать на int. Примерно так:

    Код (C++):

    int B_0[3]={1,1,1};
    int B_1[3]={0,0,0};
    int B_2[3]={-1,-1,-1};
    long A_1[3]={-185733901, -184709149, -184506849};
    long A_2[3]={992295372, 992026524, 984382369};


    long reflect(int val)
    {
        long result = val;
        // тут с числом val делаем преобразования, соответствующие формуле
        // val / 204.6 - 2.5, только без плавающей точки
       
        return result;
    }

    //---Получаем данные------
    for (int i=0; i < col; i++)
    {
       sigIn[i] = analogRead(pinA1);///204.6-2.5;
    }
    //---Фильтрация------
    const int col = 10000;
    int sigOut[10000]={};

       sigOut[0]=sigIn[0];
       sigOut[1]=sigIn[1];
       for (int i=2; i < col; i++){
          for (int z=2; z < 3; z++)
          {
             long sigOut_I_1 = reflect(sigOut[i-1]);
             long sigOut_I_2 = reflect(sigOut[i-2]);
             long sigIn_I = reflect(sigIn[i]);
             long sigIn_I_1 = reflect(sigIn[i-1]);
             long sigIn_I_2 = reflect(sigIn[i-2]);
           
             long computed = -A_1[z]*sigOut_I_1-A_2[z]*sigOut_I_2+B_0[z]*sigIn_I+B_1[z]*sigIn_I_1+B_2[z]*sigIn_I_2;
           
             sigOut[i] = map(computed,-2147483647,2147483647,-32767,32767);
          }
       }
     
     
    un1x и ИгорьК нравится это.
  18. un1x

    un1x Нерд

    Посидел, подумал, посчитал - получается что я даже теоретически не смогу определить массив int sigIn[10000] т.к. 2 байта * 10000 ~ 20 кбайт памяти. А на борту всего 8кб. Т.е. задумка провальна в любом случае, единственное решение переход на другой МК, я правильно понимаю?
     
  19. rkit

    rkit Гуру

    Тут не нужно хранить больше трех последних значений.
     
  20. DIYMan

    DIYMan Guest

    Лучше - сразу да, другой камень. Если в рамках парадигмы ардуино - возьмите Arduino Due, если не пугает высокий порог вхождения - то STM32 ;) Можно и на ESP пересесть, в принципе ;)

    Но! Всегда можно подумать и отказаться от массива совсем, выплёвывая данные во внешний мир по мере поступления и расчётов ;) Если, конечно, это применимо.