Arduino мега-очередь - PullRequest
       15

Arduino мега-очередь

1 голос
/ 03 сентября 2011

Я написал этот простой код, который считывает длину из инфракрасного датчика Sharp , конец представляет собой средний метр в сантиметрах (единица измерения) по серийному номеру.

Когда вы пишете этот код для платы Arduino Mega, Arduino запускает мигающий светодиод (вывод 13), и программа ничего не делает. Где ошибка в этом коде?

#include <QueueList.h>

const int ANALOG_SHARP = 0; //Set pin data from sharp.
QueueList <float> queuea;
float cm;
float qu1;
float qu2;
float qu3;
float qu4;
float qu5;

void setup() {
    Serial.begin(9600);
}

void loop() {
    cm = read_gp2d12_range(ANALOG_SHARP); //Convert to cm (unit).
    queuea.push(cm); //Add item to queue, when I add only this line Arduino crash.
    if ( 5 <= queuea.peek()) {
        Serial.println(average());
    }
}

float read_gp2d12_range(byte pin) { //Function converting to cm (unit).
    int tmp;

    tmp = analogRead(pin);
    if (tmp < 3)
        return -1; // Invalid value.

    return (6787.0 /((float)tmp - 3.0)) - 4.0;
}

float average() { //Calculate average length
    qu1 += queuea.pop();
    qu2 += queuea.pop();
    qu3 += queuea.pop();
    qu4 += queuea.pop();
    qu5 += queuea.pop();

    float aver = ((qu1+qu2+qu3+qu4+qu5)/5);
    return aver;
}

Ответы [ 3 ]

4 голосов
/ 04 сентября 2011

Я согласен с ошибкой peek() -> count(), указанной vhallac.Но я также укажу, что вам следует рассмотреть усреднение по степеням 2, если нет веских оснований для иного решения.

Причина в том, что на микроконтроллерах деление происходит медленно.Усредняя по степени 2 (2,4,8,16 и т. Д.), Вы можете просто вычислить сумму, а затем сдвинуть ее в битах.

Для вычисления среднего значения 2: (v1 + v2) >> 1

Чтобы вычислить среднее значение 4: (v1 + v2 + v3 + v4) >> 2

Чтобы вычислить среднее значение n значений (где n - степень 2), просто сдвиньте бит вправо вправо на [log2 (n)].

Пока тип данных для вашей переменной sum достаточно велик и не переполняется, это намного проще и намного быстрее.

Примечание : это не будет работать дляплавает в общем.На самом деле, микроконтроллеры не оптимизированы для поплавков.Вам следует подумать о преобразовании из int (что, как я полагаю, вы читаете АЦП) в значение с плавающей точкой в ​​конце после усреднения , а не до этого.

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

Other:

Вы используете оператор += без инициализациипеременные (qu1, qu2 и т. д.) - рекомендуется инициализировать их, если вы собираетесь использовать +=, но похоже, что = будет работать нормально.

Для чисел с плавающей запятой я написал бы функцию average как:

float average(QueueList<float> & q, int n)
{
    float sum = 0;
    for(int i=0; i<n; i++)
    {
        sum += q.pop();
    }

    return (sum / (float) n);
}

И назвал бы ее: average(queuea, 5);

Вы можете использовать это, чтобы усреднить любое число показаний датчика и позжеиспользуйте тот же код для последующего усреднения чисел в совершенно другом QueueList.Передача количества показаний для усреднения в качестве параметра действительно пригодится в случае, если вам нужно настроить его.

TL; DR:

Вот как я бы это сделал:

#include <QueueList.h>

const int ANALOG_SHARP=0;   // set pin data from sharp
const int AvgPower = 2;     // 1 for 2 readings, 2 for 4 readings, 3 for 8, etc.
const int AvgCount = pow(2,AvgPow);

QueueList <int> SensorReadings;


void setup(){
    Serial.begin(9600);
}

void loop()
{
    int reading = analogRead(ANALOG_SHARP);
    SensorReadings.push(reading);

    if(SensorReadings.count() > AvgCount)
    {
        int avg = average2(SensorReadings, AvgPower);
        Serial.println(gpd12_to_cm(avg));
    }
}

float gp2d12_to_cm(int reading)
{
    if(reading <= 3){ return -1; }

    return((6787.0 /((float)reading - 3.0)) - 4.0);
}

int average2(QueueList<int> & q, int AvgPower)
{
    int AvgCount = pow(2, AvgPower);
    long sum = 0;
    for(int i=0; i<AvgCount; i++)
    {
        sum += q.pop();
    }

    return (sum >> AvgPower);
}
1 голос
/ 03 сентября 2011

Вы используете queuea.peek() для получения количества. Это вернет только последний элемент в очереди. Вы должны использовать queuea.count() вместо.

Также вы можете рассмотреть возможность изменения условия tmp < 3 на tmp <= 3. Если tmp равно 3, вы делите на ноль.

0 голосов
/ 05 сентября 2011

Огромное улучшение, но первый вопрос, который у меня возник, заключается в том, почему вместо массива int следует использовать queuelist.

В качестве примера я бы сделал следующее:

int average(int analog_reading)
{
    #define NUM_OF_AVG 5
    static int readings[NUM_OF_AVG];
    static int next_position;
    static int sum;

    if (++next_position >= NUM_OF_AVG)
    {
        next_position=0;
    }
    reading[next_position]=analog_reading;

    for(int i=0; i<NUM_OF_AVG; i++)
    {
        sum += reading[i];
    }
    average = sum/NUM_OF_AVG
}

Теперь я вычисляю новое скользящее среднее при каждом чтении, и оно устраняет все проблемы, связанные с динамическим распределением памяти (фрагментация памяти, отсутствие доступной памяти, утечки памяти) во встроенном устройстве.

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

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

Во-вторых, я полагаю, что большинство современных оптимизирующих компиляторов будут делать изменения, если это возможно, я знаю, что GCC делает.

Я оставлю рефакторинг для цикла for для следующего парня.

...