一元一次方程处理设备校准数据
后台回复开源获取源代码
设备校准的一般方案
一般来说,设备未校准前,电压、电流的测量与控制,与实际值存在偏差,校准就是找到实际值的数学关系式 f(x)。
针对电压、电流的测量:
市面上ADC芯片的基本都是线性的
对ADC量程分段,基本能满足精度要求
被测量也可分段,如万用表的量程
针对其输出控制:
软件闭环控制方案,只要ADC测量精度高即可
硬件闭环控制,一般DAC输出参考量,与反馈信号综合控制输出
那么这里头:
写入DAC的值、从ADC读出的值,称为原始值 x
实际的待测量、控制量,称为真实值 y
也就有 y = k x + b
校准表格的由来
上面我们找到了关系式,一元一次方程,我们校准就是:
限定定义域,依据线性度打断点,进行分段
每一小段套用 y = k x + b
断点是坐标点,作为校准点
坐标点可用Excel拉一个表格
表格的概念出来,形式如下:
结构体描述Excel表格
我们设想,若MCU内部维护一个表格,那么我们就可以愉快地使用校准数据。
尝试用结构体描述上述表格:
typedef struct
{
int32_t actual; /* 实际的目标值 */
uint32_t original; /* 原始值 */
}CalPoint_t;
typedef struct
{
uint16_t start_addr; /* EEPROM上的存储地址 */
uint8_t point_size; /* 校准点的大小 */
uint8_t number; /* 校准点数 */
uint8_t ch; /* 通道 */
uint8_t linkage_ch; /* 联动通道 */
CalPoint_t *point; /* 校准点 */
}CalTable_t;
typedef struct
{
uint8_t ch_number; /* 通道数 */
CalTable_t *table; /* 校准表格 */
}CalObj_t;
接着定义宏列表,具体参考上篇 宏列表批量定义结构体数组、枚举 ,结构上基本与Excel表格一致:
概述
上篇 宏列表批量定义结构体数组、枚举 已设计好校准表格
/* 用户校准表格控制块。结构已固定 */
static CalObj_t __userCalTable[] = {
CAL_TABLE(TABLE_USER_REG, __NULL, __NULL, __NULL, __NULL)
};
现在需要从“__userCalTable”中获取真实值、原始值,包括从存储器加载校准数据等等。
获取真实值
“ry_get_actual”函数是一个分段函数,扔3个变量给它,它吐出结果
cal_table:告诉它是哪个校准表格
ch:告诉它通道号
val:原始值 x
/* 获取实际电压值、电流值 */
float ry_get_actual(uint8_t cal_table, uint8_t ch, uint32_t val)
{
uint8_t pos, number;
int32_t prev, next;
float k;
CalPoint_t *CalPoint;
CalObj_t *obj;
__IS_CHANNEL(cal_table, ch, 0);
CalPoint = obj->table[ch].point;
number = obj->table[ch].number;
/* 找出在哪个区间内 */
for(pos = 0; pos < number; pos++)
{
if(val <= CalPoint[pos].original)
{
if(val == CalPoint[pos].original)
return (float)CalPoint[pos].actual;
if(pos == 0)
pos = 1;
// return ((float)CalPoint[0].actual) / CalPoint[0].original * val;
break;
}
}
if(pos >= number)
pos = number - 1;
/* y = k * x + b */
/* k = (y2 - y1) / (x2 - x1) */
/* b = y1 - k * x1 */
/* f(data) = k(data - x1) + y1 */
prev = CalPoint[pos-1].actual;
next = CalPoint[pos].actual;
k = (float)(next - prev) / (float)(CalPoint[pos].original - CalPoint[pos-1].original);
return (float)((float)((int32_t)val - (int32_t)CalPoint[pos-1].original) * k + prev);
}
加载校准数据
从存储器中加载校准数据,放入表格中:
/* 获取校准状态,并加载校准数据 */
uint8_t ry_get_device_cal_status(void)
{
uint8_t pos;
uint8_t buf[1];
ry_eeprom_read(DEVICE_CAL_DATA_ADDR, buf, 1);
if(buf[0] == DEVICE_CAL_FINISH)
{
/* 加载校准数据 */
for(pos = 0; pos < __CAL_TABLE_NUMBER; pos++)
{
__DeviceCalFlag = ry_cal_table_upload(pos);
}
return __DeviceCalFlag;
}
__DeviceCalFlag = DEVICE_CAL_NO;
return __DeviceCalFlag;
}
举例
对照校准表格:
编写主函数:
运行结果如下:
可以看到校准表格:
一元一次方程正常运转
能找点,能转换出正数、负数
-END-