问题描述

在使用PCF8591不断轮流读取两个通道时, 发现数据出现了错位的问题

例如:

1
2
ad_p=Get_AD(1) //读取通道1的值
ad_u=Get_AD(3) //读取通道3的值

可是使用数码管显示数据会发现一个很神奇的问题

ad_p显示的是通道3的ad值

ad_u显示的是通道1的ad值

两个数据出现了错位, 这不是我们想看到结果

问题定位

打开数据手册7.4节关于ad转换的描述可以看到如下一段话:

image-20241219010803540

译文:

  1. 读取周期中传输的第一个字节包含前一个读取周期的转换结果代码

  2. 上电复位后,读取的第一个字节是十六进制 80。

原来PCF8591的AD转换需要时间, 所以会将转换结果储存到寄存器中, 下一次读取时直接将寄存器中的值直接发送出去

将开发板接上逻辑分析仪抓取波形验证

pcf

可以发现上电后第一次的读取结果确实是0x80, 而第二次读取的才是第一次设定的通道一的值, 从而导致之后的数据全是错位的

问题解决

既然读取寄存器值的是上一次的值, 我们可以直接获取两次AD值就可以保证变量值一定是我们要的通道, 但是连续使用两次Get_AD();函数会产生很多性能开销, 所以下面给出更加合理的解决办法

原理

单片机接收数据时, 如果发送了应答, 那么从机就会继续将寄存器的值发送回来, 我们只要第一次继续应答, 而第二次取消应答便可以实现快速读取两次寄存器从而覆盖掉上一次通道的AD值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
unsigned char Get_AD(unsigned char channel){
unsigned char AD=0;
static long int i=0;
I2CStart();
I2CSendByte(0x90);I2CWaitAck();
I2CSendByte(0x00|channel);I2CWaitAck();
I2CStop();

I2CStart();
I2CSendByte(0x91);I2CWaitAck();
AD=I2CReceiveByte();I2CSendAck(0);//发送应答, 继续接收数据
AD=I2CReceiveByte();I2CSendAck(1);//不应答, 终止通讯
I2CStop();
return AD;
}