一、 基础型
队列定义:
#define DATA_LIST_MAX 64
static uint8_t data_list[DATA_LIST_MAX] = {0};
static uint8_t data_index = 0;
数据进出:
uint8_t data_list_access(uint8_t data)
{
uint8_t temp;
data_index = (data_index >= _countof(data_list)) ? 0 : data_index;
temp = data_list[data_index];
data_list[data_index] = data;
data_index++;
return temp;
}
应用于均值滤波:
static uint8_t data_average = 0;
// ==========================================================================================================
// 初始化完成后,每次都将新值放入队列、并求出均值
//
// 运算过程说明(假设队列长度为n = 8):
// 1、之前的均值age1 = (a0 + a1 + ... + a7) / n // 包含ai
// 2、现在有了新值data_new,我们将它插入第i个位置、ai被替换成了data_new
// 那么此时的均值age2 = (a0 + a1 + ... + a7 + data_new) / n // 不包含ai
// = (a0 + a1 + ... + a7 + data_new + ai - ai) / n
// = (a0 + a1 + ... + a7 + ai + data_new - ai) / n
// = age1 + (data_new- ai) / n
// 也就是说我们不需要每次都重新计算数据的和
// 3、有符号数不能用移位来优化除法,所以这里使用uint16_t 配合三目运算符来求差值
// 负数是以补码而不是原码的形式存储并参与运算,所以不能用移位替代除法
// 同时有符号数的操作也慢些
//
// ==========================================================================================================
uint8_t data_list_average(uint8_t data)
{
uint16_t data_old;
uint16_t data_new = data;
uint16_t average = data_average;
data_old = data_list_access(data);
if(data_new >= data_old)
{
average += (data_new - data_old) / _countof(data_list); // 变大
}
else
{
average -= (data_old - data_new) / _countof(data_list); // 变小(average遇到突然变得很小的数值怎么办。。。。。)
}
data_average = (uint8_t)average;
}
二、 公用型
多个环形队列公用同一组滑动窗口处理函数
队列定义:
typedef struct
{
uint8_t *list; // 用于遍历队列
uint8_t size;
uint8_t index;
uint8_t average;
}T_DATA_LIST_ITEM, *pT_DATA_LIST_ITEM;
// 具体的某一个用户
#define DATA_LIST_USER01_MAX 64
static uint8_t ram_list_user01[DATA_LIST_USER01_MAX] = {0};
static T_DATA_LIST_ITEM list_user01 = { .list = &ram_list_user01[0], // 第一个元素
.size = sizeof(ram_list_user01),
.index = 0, // 第一个元素
.average = 0,
};
数据进出:
uint8_t data_list_access(pT_DATA_LIST_ITEM p_list, uint8_t data)
{
uint8_t temp;
p_list->index = (p_list->index >= p_list->size) ? 0 : p_list->index;
temp = *(p_list->list + p_list->index);
*(p_list->list + p_list->index) = data;
p_list->index++;
return temp;
}
应用于均值滤波:
// ========================================================================================================== // 初始化完成后,每次都将新值放入队列、并求出均值 // // 运算过程说明(假设队列长度为n = 8): // 1、之前的均值age1 = (a0 + a1 + ... + a7) / n // 包含ai // 2、现在有了新值data_new,我们将它插入第i个位置、ai被替换成了data_new // 那么此时的均值age2 = (a0 + a1 + ... + a7 + data_new) / n // 不包含ai // = (a0 + a1 + ... + a7 + data_new + ai - ai) / n // = (a0 + a1 + ... + a7 + ai + data_new - ai) / n // = age1 + (data_new- ai) / n // 也就是说我们不需要每次都重新计算数据的和 // 3、avr gcc编译器不会优化有符号数的除法,所以这里使用uint8_t来求差值 // 负数是以补码而不是原码的形式存储并参与运算,所以不能用移位替代除法 // 同时有符号数的操作也慢些 // // ========================================================================================================== vodi data_list_average(pT_DATA_LIST_ITEM p_list, uint8_t data) { uint8_t data_old; uint8_t data_new = data; uint8_t average = p_list->average; data_old = data_list_access(p_list, data); if(data_new >= data_old) { average += (data_new - data_old) / p_list->size; // 变大 } else { average -= (data_old - data_new) / p_list->size; // 变小(average遇到突然变得很小的数值怎么办。。。。。) } p_list->average = average; }使用举例:
data_list_average(&list_user01, get_data());
三、 普适型
struct中增加一个type参数来说明每个数据的长度,例如:type = sizeof(ram_list_user01[0])。再使用void *list来指向任意类型的数据(类型强制转换:list = (void *)(&ram_list_user01[0])。这样就可以处理任意类型的数据的滑动窗口了。这属于面向对象的设计么……?