一般情况下,我们会先收集并存储所有数据,然后将这些数据全部传递给一个函数。该函数会找出其中的最大值和最小值,并对其他数据进行累加,最后求出平均值。
现在我们来考虑另一种情况:周期性地获取一个数据,当达到指定数量后,直接计算出平均值,而不保存实时数据。
一、实现
typedef struct
{
uint8_t u8Cnt;
uint16_t u16Min;
uint16_t u16Max;
uint32_t u32Sum;
uint16_t u16Avg;
} Avg1Word_t;
#define mMath_Avg1Init(pObj) {(pObj)->u8Cnt = 0; (pObj)->u16Avg = 0;}
首先,我们定义一个上述结构体的变量,并调用 mMath_Avg1Init 对其进行初始化;
bool Math_Average1Apply(Avg1Word_t *pObj, uint16_t u16New, uint8_t u8Num)
{
if (pObj->u8Cnt == 0)
{
pObj->u16Min = u16New;
pObj->u16Max = u16New;
pObj->u32Sum = u16New;
}
else
{
if (pObj->u16Min > u16New)
{
pObj->u16Min = u16New;
}
if (pObj->u16Max < u16New)
{
pObj->u16Max = u16New;
}
pObj->u32Sum += u16New;
}
pObj->u8Cnt++;
if (pObj->u8Cnt >= u8Num)
{
if (pObj->u8Cnt > 2)
{
pObj->u32Sum -= pObj->u16Min;
pObj->u32Sum -= pObj->u16Max;
pObj->u8Cnt -= 2;
}
pObj->u16Avg = pObj->u32Sum / pObj->u8Cnt;
pObj->u8Cnt = 0;
return true;
}
return false;
}
其次,每当有新数据产生时,我们会调用 Math_Average1Apply 函数,并将新数据u16New传入该函数。
同时,我们还会指定一个总数u8Num,例如 u8Num = 10,表示调用 Math_Average1Apply 函数10次后,产生一个新的平均值。在这 10 次调用中,我们会去掉最大值和最小值,然后对剩余的 8 个数求平均值;
当 u8Num <= 2 时,不会去掉最大值和最小值,而是直接对所有数据求平均值;
#define mMath_GetAvg1Value(pObj) ((pObj)->u16Avg) // Get average value
最后,当 Math_Average1Apply 函数返回 true 时,可以使用以上的宏定义来获取当前的平均值。
二、验证
用于验证这个算法的程序如下:我们循环调用 Math_Average1Apply 函数若干次,当函数返回 true 时,打印出相应的结果。
static void TestMath_privAverage1(uint16_t * pBuf, uint8_t u8Len)
{
Avg1Word_t oAvg;
uint8_t u8Cnt;
mMath_Avg1Init(&oAvg);
for (u8Cnt = 0; u8Cnt < u8Len; u8Cnt++)
{
// 原始数据
printf("%d ", pBuf[u8Cnt]);
if (Math_Average1Apply(&oAvg, pBuf[u8Cnt], u8Len))
{
// 输出平均值
printf("-> Average: %d\n", mMath_GetAvg1Value(&oAvg));
}
}
}
你可以修改 trBuffer1 数组的内容,进一步验证这个算法的正确性。
static uint16_t trBuffer1[] = { 0x0123, 0x4567, 0x89ab, 0xcdef };
TestMath_privAverage1(trBuffer1, ARRAY_SIZE(trBuffer1));
完整的代码和验证程序,请参考 chip_c: 芯片C语言框架和实用程序集合