[英]Arduino mega queue
我编写了这个简单的代码,该代码从Sharp红外传感器读取一个长度,最后以串行形式显示以厘米(单位)为单位的平均米。
当为Arduino Mega开发板编写此代码时,Arduino启动一个闪烁的LED(引脚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;
}
我同意vhallac列出的peek()
-> count()
错误。 但我还要指出,除非有很强的理由,否则您应该考虑以2的幂进行平均。
原因是在微控制器上,除法很慢。 通过对2(2、4、8、16等)的幂进行平均,您可以简单地计算出总和,然后对其进行位移位。
计算2的平均值: (v1 + v2) >> 1
计算4的平均值: (v1 + v2 + v3 + v4) >> 2
要计算n个值的平均值(其中n是2的幂),只需将总和右移[log2(n)]。
只要您的sum变量的数据类型足够大且不会溢出,这将变得更加容易和快捷。
注意 :一般而言,这不适用于浮点数。 实际上,微控制器并未针对浮点数进行优化。 您应该考虑从int(我假设您正在读取ADC的数据)转换为在平均值之后而不是之前的末尾浮动。
通过将int转换为float,然后对float进行平均,与将int转换为float相比,与平均int相比,您损失的精度更高。
其他:
您正在使用+=
运算符而不初始化变量( 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);
}
您正在使用queuea.peek()
获取计数。 这只会返回队列中的最后一个元素。 您应该改为使用queuea.count()
。
您也可以考虑将条件tmp < 3
更改为tmp <= 3
。 如果tmp
为3,则除以零。
吉德沃兹(Jedwards)取得了很大的进步,但是,我的第一个问题是,为什么要使用队列列表而不是整数数组。
例如,我将执行以下操作:
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会这样做。
我将把重构留给下一个家伙。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.