分享

巨省钱的PCF8591差分电压实验

 新用户5228KeDY 2022-02-15

PCF8591是一个超级古老的器件,我是在很久前买一个51学习板的时候买的它。当时也没有学习明白,只是会了单端测量电压。

把它翻出来的原因,是一时想找个短时可玩之物,却又没有其他的东西在手边;另外有些东西太耗时了,不能玩,万一沉迷就不好。所以寻PCF8591的门上去。

淘宝上的PCF8591比较可取的是这种模块,主要是便宜,整个模块的价格和一个DIP芯片的价格相同。这个模块自带热敏电阻、光敏电阻、电位器和一个接地输入测量,可以极为简单地进行这些东西的相关实验,也包括PCF8591如何进行单端测量电压的I2C代码实验。

我现在想再去试试它,就是因为它的I2C通信,可以直接移植到microBlocks里面去。

有很多的PCF8591的古老例子,但都是配置成单端的,没有看到有差分应用的。很奇怪。

查找了几个文章,又找了一下PCF8591的数据手册——这个模块可能是因为应用得较早、较广、较多的缘故,中文资料比较多,但是仍然没看到差分应用的、可以拿来即用的例子——因为硬件知识极度的匮乏,所以期望有拿来即用的东西。

后来弄明白(也许是错误的)要突破的地方,是以下几处:接线、控制字、返回值。

以下的实验,需要把那4个跳线帽全部取下。

一、接线图

淘宝上的模块,直接按标识接线就行了,默认I2C地址是0x48,也就是0x90>>1。当直接使用DIP时,A0、A1、A2接VDD和GND情况,决定其地址。见原手册第13、第14页。另外它的参考电压就是VCC,单端量程是0~5V,差分是+-2.5V。

二、控制字

使用双通道差分电压,读通道0,即A0和A1间电压0b00110000,即0x30;读通道1,即A2和A3间电压0b00110001,即0x31。

PCF8591还有DAC功能,控制字配置在手册中另有介绍。

三、返回值

PCF8591是8位ADC,这比Arduino的10位还要差,但是Arduino不能测差分,不能测负电压,并且,虽然是8位ADC,经过测试,测量还是很准确的。

因为配置成了两路差分,所以得区分出来负电压来。手册第10页给了AD对应的数据,当返回值大于0x80即127时,返回的就是负电压;最大值0xFF即255对应的就是零,且当输入电压大于或等于参考电压的一半时,都返回0x80,也就是,它的量程就是正、负参考电压的一半。

四、代码

那些注释是笔记和学习过程。

#include "Wire.h"
#define PCF8591 (0x90 >> 1)
//I2C总线地址,其实就等于0x48
byte value0, value1;
//用来存储读回来的byte

#include "MovingAverageFloat.h"
//现成滤波库,搜索安装movingaveragefloat
//自己写也并不麻烦,循环移动取平均即可
//不滤波也是可以的,但噪声大,主要表现为正弦噪声
MovingAverageFloat <4> filter0;
MovingAverageFloat <4> filter1;
//如果不介意读取速度,可取8,或16等
///////////////////////////////////////
void setup()
{
  Wire.begin();
  Serial.begin(9600);
}
void loop()
{
  float Ch0 = 0.0;
  float Ch1 = 0.0;
  //存储最终转换后的电压值
  Wire.beginTransmission(PCF8591);
  Wire.write(0b00110000);
  //配置成两个差分通道,不自增,读通道0
  Wire.endTransmission();
  Wire.requestFrom(PCF8591, 2);
  value0 = Wire.read();
  value0 = Wire.read();
  //空读1次,不知哪块儿说的,不读不行,不知为何
  Wire.endTransmission();
  if (value0 > 127) {
    value0 = 255 - value0;
    Ch0 = (float)(value0) * (-1.0) / 255.0 * 5.0;
  } else {
    Ch0 = (float)(value0) / 255.0 * 5.0;
  };
  //区分正负电压,量程+-2.5V,超过都返回2.5V
  delay(1);
  //实际应该够I2C的5us即可,经测试,去掉也没影响
//也有说必须等5ms的,不太懂
  Wire.beginTransmission(PCF8591);
  Wire.write(0b00110001);
  //配置为两个差分,不自增,读通道1
  Wire.endTransmission();
  Wire.requestFrom(PCF8591, 2);
  value1 = Wire.read();
  value1 = Wire.read();
  //和上面空读道理相同
  Wire.endTransmission();

  if (value1 > 127) {
    value1 = 255 - value1;
    Ch1 = (float)(value1) * (-1.0) / 255.0 * 5.0;
  } else {
    Ch1 = (float)(value1) / 255.0 * 5.0;
  };
  //仍然是负电压转换
  Serial.print(filter0.add(Ch0));
  //平均值
  Serial.print(",");
  //逗号是为了串口绘图器能同时画两个信号
  Serial.println(filter1.add(Ch1));
  //Serial.println("-----------");
  //调试用的,SSCOM里面可以打开看看
  delay(1);
  //也可以去掉,放这儿吧,不影响啥
}

五、结果

串口绘图器画出来的两组差分电压的结果。当一个通道测量而另一通道悬空时,悬空通道会出现杂乱读数;当一个通道测量时将另一通道直接对接在一起,能得到读数为零。

结果说明8位ADC确实解决了Arduino本身不能解决的电压测量难题:差分测量和负电压测量,上面的测量与实际万用表对比,误差出现在百分之一伏特上,按照8bit计算,精度应该在0.02V上,但是由于一些原因、包括以Arduino板上5V输出作为参考电压也不稳定,这个测量精度达不到0.02V,但明显是可以使用的。

PCF8591手册上的说法是速度只受限于I2C速度,但好像没那么快:中学物理实验要求高速采集数据的也不是太多——以PCF8591的能力应对电容器充放电、自感实验的亮一下应该可以;最重要的,它性价比极度的高!在某宝上,这种PCF8591模块或DIP只需要ADS1115的二分之一到十分之一的价格。而且,搭配一只小阻值电阻,如10欧,学生可以用ESP32和microBlocks自嗨绝大部分伏安法及电磁相关的实验了。等等有闲,把它移植到microBlocks里面去。这种极便宜的器件,值得再挖掘后普及使用。

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多