PCF8591多通道数据读取异常问题

2023/11/28

问题描述

PCF8591在循环读取两个通道时,两个通道数据出现交错问题。
例如我们想实现:第一次读取通道一、第二次读取通道二、第三次读取通道一、第四次读取通道二……依次循环
但实际数据:第一次读取的值为0x80、第二次读取的值为通道一的值、第三次读取的值为通道二的值、第四次读取的值为通道一的值……

解决方法

在2003版的PCF8591数据手册第九页我们可以找到如下描述:
请添加图片描述
大体为:1. 我们本次读取的ADC值为上次转换的ADC值。 2. 第一次读取的值由于没有上次转换,因此上电后第一次读取的值默认为0x80。
也就是说,由于ADC有转换时间,当我们在调用PCF的读取函数时,PCF并不能立刻采样此时的ADC值并返回给我们,而是把上次ADC转换后存储到寄存器里的值返回给了我们,第二次读取时返回的是第一次的值,第三次读取时返回的是第二次的值。但此时也出现了一个问题,我们在第一次读取时,之前并没有ADC采样,没法返回给我们相应的值。因此,PCF在制造时规定,当我们在第一次读取时,默认返回 0x80,也就是十进制的128,刚好是采样8位精度的一半即128/255,这也就是为什么当我们在刚上电时,5V系统下PCF8591读取出来的值是2.5V。
我们可以通过逻辑分析仪抓取到的数据进行验证,可以看到,在第一次读取通道三(即控制字为0x43)时读取到的值为0x80,在第二次读取通道零(即控制字为0x40)时读取到的值是0xFF,实际上这个值是通道三的值。请添加图片描述
至此,我们已经明白了为何从PCF8591的多通道读取到的值是错误的,明白了原理,相应的解决办法也就不再困难。
最简单的方法,既然本次读取到的数据是上次的,那我们可以连续读取两次,舍弃第一次读取到的数据,保留第二次读取到的数据。因为第一次的数据是上个通道的,但第二次的数据确确实实是我们想读取的通道的。
上述方法虽然简单,但也有一个较大的缺陷,也就是两次读取到的数据中只有一个是我们想用的,另外一次的数据都是需要被舍弃的,浪费系统资源。其实我们只需要牢记本次读取的值为上次转换得到的即可,假设我们想依次读取三个通道里的值,只需要记住第一次读取的值为默认的0x80,第二次的值为通道一,第三次的值为通道二,第四次为通道三,第五次为通道一,第六次为通道二……以此类推,再做出相应的操作即可。

总结

对于不熟悉的芯片,杜绝主观臆断,一定要大体浏览下芯片手册,要按照芯片手册要求来。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
PCF8591是一种集成了AD转换器、DA转换器和数字I/O接口的芯片。它有4个输入通道,其中3个是AD转换通道,1个是DA转换通道。下面是三通道AD转换的代码: ```c #include <Wire.h> // 引入Wire库 #define ADDR 0x48 // PCF8591的地址为0x48 void setup() { Wire.begin(); // 初始化I2C总线 Serial.begin(9600); // 初始化串口 } void loop() { byte value1, value2, value3; // 定义三个变量用于存储AD转换结果 Wire.beginTransmission(ADDR); // 开始向PCF8591发送数据 Wire.write(0x40); // 选择通道0 Wire.endTransmission(); Wire.requestFrom(ADDR, 1); // 请求1个字节数据 value1 = Wire.read(); // 读取数据 Wire.beginTransmission(ADDR); // 开始向PCF8591发送数据 Wire.write(0x41); // 选择通道1 Wire.endTransmission(); Wire.requestFrom(ADDR, 1); // 请求1个字节数据 value2 = Wire.read(); // 读取数据 Wire.beginTransmission(ADDR); // 开始向PCF8591发送数据 Wire.write(0x42); // 选择通道2 Wire.endTransmission(); Wire.requestFrom(ADDR, 1); // 请求1个字节数据 value3 = Wire.read(); // 读取数据 Serial.print("Value1: "); // 打印AD转换结果 Serial.println(value1); Serial.print("Value2: "); Serial.println(value2); Serial.print("Value3: "); Serial.println(value3); delay(1000); // 延时1秒 } ``` 在这个代码中,我们使用Wire库来进行I2C通信。首先,在setup()函数中初始化I2C总线和串口。然后,在loop()函数中,我们依次选择通道0、1、2,并请求1个字节的数据读取AD转换结果,并打印出来。最后,延时1秒后再进行下一次转换
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值