上篇帖子主要说道了物理性的硬件方面的东西,当然,那不是全部,还有些可能遇到的情况以后还会慢慢提及。现在开始说编程
- 预定义
为了以后说明方便,先预定义一些需要用到的东西。不过这里没什么好研究的,有经验的坛友也可以通过常量的名字进行望文生义。
#define P_WEIGHT_CLK RCC_AHBPeriph_GPIOA
#define P_WEIGHT GPIOA
#define PP_WEIGHT_SDA GPIO_Pin_0
#define PP_WEIGHT_SCL GPIO_Pin_1
#define PP_WEIGHT_ADRST GPIO_Pin_2
#define SDA_0 GPIO_ResetBits(P_WEIGHT, PP_WEIGHT_SDA)
#define SDA_1 GPIO_SetBits(P_WEIGHT, PP_WEIGHT_SDA)
#define SCL_0 GPIO_ResetBits(P_WEIGHT, PP_WEIGHT_SCL)
#define SCL_1 GPIO_SetBits(P_WEIGHT, PP_WEIGHT_SCL)
#define RST_0 GPIO_ResetBits(P_WEIGHT, PP_WEIGHT_ADRST)
#define RST_1 GPIO_SetBits(P_WEIGHT, PP_WEIGHT_ADRST)
- 取值
ADS1230是一个20bit的ADC芯片,取值前,初始化时,需要先对ADC进行复位,并等待数据就绪。以下的代码只是参考,关于信号脉宽方面的问题,大家自行调试,这个涉及到PCB板整板的特性方面的问题,我无法总而言之。
ADC复位:
RST_0;
Nop(5);
RST_1;
Nop(50);
等待数据就绪:
uint8 i;
while(GPIO_ReadInputDataBit(P_WEIGHT, PP_WEIGHT_SDA)==Bit_SET);
for(i=0; i<26; i++)
{
SCL_1;
SCL_0;
}
初始化完成后,我们就可以坐等外中断发生了。一旦发生外中断,我们就赶紧取数。
SCL_0;
AdWeight = 0;
for(i=0; i<20; i++)
{
SCL_1;
AdWeight <<= 1;
SCL_0;
if(GPIO_ReadInputDataBit(P_WEIGHT, PP_WEIGHT_SDA)==Bit_SET) AdWeight++;
}
for(i=0; i<4; i++) { SCL_1; SCL_0; }
ADC的值被保存在int32类型的变量AdWeight中。对于AdWeight值的含义,我们需要仔细阅读相关手册。这里,小于2.5V的信号会被解释为负值。但是,我们只接收了20bit的值,却放在一个32bit的变量中,这样就成了正值。这不对,我们得处理一下。
if(AdWeight > 0x7ffff) AdWeight |= 0xFFF00000;
- 处理零抖
能取值你以为就完事了吗?
在称重秤上不放任何东西,你会发现,这些值并不固定,完全不是零的样子。
当然这些值不是零的样子!如果多换几个传感器试试,甚至还有可能出现负值,而且至少,你的秤盘自重在传感器上还施加了一个力!
不过这些都不是最可怕的!最可怕的是,这些值会不断地发生变化!
这是什么梗?其实,这也不是什么大梗。做传感器做多了,大抵应该能适应,一些物理量在环境不稳定的时候会出现抖动或漂移。去掉就行了。
去零抖怎么去?天知道取出来的值,是信号抖动,还是称重?
嗯!没错!天是不知道的,但是你知道!
我们都可以这样知道:零抖信号的特点就是一不稳定,二不会突变,三是小到一个境界。记住这个原则,你在即时取值的时候,判断并减去,你就会发现,你的称重零的样子,出现了!
三、标定确定传感器的线性参数
有效去掉零抖后,我们所获得的测值,就纯净多了!接下来,我们就要将ADC的转换值,与实际的重量相对应。这里我们所说的实际重量,不能用随意的重量来制造,我们得用标准砝码。砝码取决于你自己购买,我买的砝码是5kg、10kg、20kg等这样的一组。
有了砝码,写一段ADC读数代码,再采集一组数,为了便于分析处理,将这组数据输入到EXCEL表格里,如图2-1(参考样例):
图2-1
计算平均值是统计学的一种常态,我就不解释了。
有了这些数据后,我们就要放大招了。先将各重量的AD值去0kg,如图2-2(参考样例):
图2-2
相信眼尖的人能从这组数据中看到一些欣喜。如果看不到也没关系,我们还可以继续分析。下面我们再看一下对5kg的倍数,如图2-3(参考样例):
图2-3
也许,尽管,我们处理数据,但是我们未必对数据有那么高的兴趣,那么我们就搞各有兴趣的图表看看。为了做图标,我们将图2-3的0数据做一个补全,如图2-4(参考样例):
图2-4
我相信excel能给我们来一张砝码-AD均值分析曲线图,不过我这里截图来自OriginPro,如图2-5(参考样例)。请大家不要指责我excel做图表的失败。
图2-5
再来一张砝码-去0AD均值分析曲线图,如图2-6(参考样例):
图2-6
这图乍一看,线性度很好,这显然值得欣喜。当然,我们体重称重常常只只要0.1kg的精度,如果不吹毛求疵的话,这个欣喜我们可以保持下去,因为我们可以使用公式y=kx+a了。
对于不同的物理结构装上传感器,k、a值是不一样的。所以我们得为每个产品进行k、a的确定。这个确定的过程在生产上就叫标定。对于线性函数控制的测量自然需要进行两点标定了。我这里选择的是0kg标定与20kg标定。
对于编程而言,保存参数也是一个活儿。建议大家多用一些struct、union的结合,让数据的涵义与字节的操作不至于转换来转换去的。例如,针对斯它姆32的字存储函数,我就定义了如下一个复合类型。
#pragma pack(2)
typedef union _ycj
{
struct
{
int32 ad20;
int16 wr;
int32 ad0;
} c;
struct
{
uint16 w[5];
} w;
}ycj;
#pragma pack(1)
这样做的好处我就不说了。我要说的是,我直接保存的是0kg的AD值与20kg的AD值,然后在初始化的时候,计算一下k,通常a就是ad0了。
当然,如果你要保存一个浮点数也是没有问题的,我相信你也能做到。四、如何在取均值的时候避免除法运算
我们都知道,除法运算是一个坑单片机的运算。如果多个除法运算高频率运用,会让单片机殚精竭虑的。所以消除除法运算很重要。让单片机的CPU使用率降低,对保证单片机各部件运行时序有很大的帮助。
对于统计求均值的除法运算,是完全可以避免的。例如,你需要取10次求一次均值,你毫无悬念的需要除以10,这无法避免。但是,你可以修改策略。例如,你可以降低一些性能,改为取8求均,这样除以8就可以用右移3位二进制位来完成了。当然,如果你想提高性能,那就取16求均好了,为什么非要用10呢?五、计算重量
计算重量没什么难事,就是在公式中要处理一下动态的0抖与你可能需要设置的去皮。
0抖的取值建议使用AD值,而去皮呢?用重量值也挺好的。因为从倍率上将,AD值较为敏感,而重量值则较为稳定。
重量计算参考公式如下:
Weight = ((double)(AD - AD0 - AD0Shake)) / k - Tare;
六、关于精度
对于重量的处理,要注意符合精度要求的数字表达形式,例如你可以全部用双精度数来表示所有数字,也可以用整型数处理一部分,用双精度数处理一部分,甚至可以将所有小数以x10的倍率全部转为整形运算。但是无论你是为了方便,或为了速度,你都要保证有效数字不能被丢弃,运算结果不溢出。七、调试中的问题
由于这是一个线性度很好的传感器,所以调试时主要代码并不有多难,但是这并不表示调试过程会有多简单。因为这是一个跟物理力**系紧密的应用,所以,任何一个制作上的不讲究,都会让你麻烦缠身,甚至焦头烂额。这个时候你一定要保持冷静,思路开阔,不要只想着撸码解决一切。
常见的问题大致如下:
1、代码造成的问题,修改代码;
2、零抖,优化零抖算法;
3、砝码称重稳定,人多次称重数值不一,检查传感器安装是否可靠,固定螺纹孔是否有很好的同心度与垂直度,秤盘刚性是否足够,如果因为踩踏变形导致传感器承受扭剪力,测值是不可能如一的;
4、因为秤盘四脚不同面,不水平,都会不同程度的造成测值不准;
5、因为环境有机械振动干扰,会造成测值不稳定;
6、电磁干扰,会造成测值偏差,甚至是大幅度偏差;
7、其他原因,有待你自己发现。八、结果展示
控制好了各个细节,我们就可以坐想其成了。下面是20kg、30kg砝码的测量结果(如图2-7):
图2-7
这是身高体重0.1公斤分辨率下的测试结果。砝码检测,非常准确。当然,人站上去就不一定了,所以一个东西的好坏判断,不能急于下结论,要多实践,问题出在哪里,要能精确定位。
---------------------
作者:yyy71cj
链接:https://bbs.21ic.com/icview-3213960-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。