PCF8591是一款单芯片、单电源、低功耗8位CMOS数据采集设备
具有四个模拟输入、一个模拟输出和一个串行12c总线接口。
三个地址引脚AO, A1和A2用于编程硬件地址,允许使用多达8个设备连接到12c总线而不需要额外的硬件。
地址、控制和数据通过两路双向12c总线串行地传送到和从设备。
该装置的功能包括模拟输入多路复用、片上跟踪和保持功能、8位模数转换和8位数模转换。最大转换速率由12c总线的最大速度给出。
标志 | 引脚 | 描述 |
---|---|---|
AIN0 | 1 | 模拟输入通道1(A/D转换器) |
AIN1 | 2 | 模拟输入通道2(A/D转换器) |
AIN2 | 3 | 模拟输入通道3(A/D转换器) |
AIN3 | 4 | 模拟输入通道4(A/D转换器) |
A0 | 5 | 硬件地址 |
A1 | 6 | 硬件地址 |
A2 | 7 | 硬件地址 |
Vss | 8 | 负电源电压 |
SDA | 9 | IIC数据输入/输出 |
SCL | 10 | IIC时钟 |
OSC | 11 | 振荡器inputioutput |
EXT | 12 | 振荡器输入的外部和内部开关 |
AGND | 13 | 模拟地 |
Vref | 14 | 参考电压输入 |
AOUT | 15 | 模拟输出(D/A转换器) |
Vdd | 16 | 正极 |
i2c总线系统中的每个PCF8591设备通过发送一个有效的地址来寻址。
地址由固定部分和可编程部分组成。
可编程部分必须根据地址引脚AO、A1和A2进行设置。
地址总是必须作为12c总线协议中的开始条件之后的第一个字节被发送。
地址字节的最后一位是读写位,它设定了接下来数据传输的方向(见下图)。
发送到PCF8591设备的第二个字节将存储在其控制寄存器中,并需要控制设备的功能。
控制寄存器的高4位用于使能模拟输出,并将模拟输入编程为单端或差分输入。
低4位选择由高4位所定义的模拟输入通道之一。
如果设置了自动增量标志,在每次A/D转换后,通道号会自动增加。
如果在使用内部振荡器的应用中需要自动增量模式,则应该设置控制字节(第6位)中的模拟输出使能标志。
这允许内部振荡器连续运行,从而防止由振荡器启动延迟导致的转换错误。
上电复位后,控制寄存器的所有位都复位为逻辑0。为了省电,D/A转换器和振荡器被禁用。模拟输出被切换到高阻抗状态。
发送到PCF8591设备的第三个字节存储在DAC数据请求器中,并使用片上D/ A转换器转换为相应的模拟电压。
这个D/A转换器由一个电阻分频链组成,
该分频链连接到具有256个分频点和选择开关的外部参考电压。tap-decoder将其中一个tap切换到DAC输出线(见下图)。
模拟输出电压由一个自动归零的放大器缓冲。
这个缓冲放大器可以通过设置模拟输出使能标志
来打开或关闭控制寄存器。
在使能状态下,输出电压被保持直到进一步的数据字节被发送。片上D/A转换器也用于逐次逼近A/D转换。为了释放DAC进行A/D转换循环,单位增益放大器配备了跟踪和保持电路。
这个电路在执行A/D转换时保持输出电压。
注意: S为IIC开始信号,P为IIC停止信号
要使用D/A转换,必须使能模拟输出,然后根据流程,首先
IIC开始信号->PCF8591地址写->等待PCF8591回应->控制字节->等待PCF8591回应->DAC的值->等待PCF8591回应->DAC的值…
这个DAC的值可以一直改变,只要没有重新IIC开始信号,或者结束信号,DAC输出就一直是最后一个输出的值!!
A/D转换器采用逐次逼近转换技术。
在A/D转换周期内临时使用片上D/A转换器和高增益比较器。
A/D转换周期总是在发送一个有效的读模式地址到PCF8591设备后开始。
A/D转换周期在应答时钟脉冲的后缘触发。
一旦转换周期被触发,所选通道的输入电压样本被存储在芯片上转换成相应的8位二进制码。
从差分输入中提取的样本被转换成一个8位的转换结果存储在ADC数据寄存器并等待传输。
如果设置了自动增量,则选择下一个通道。
在一个读周期中传输的第一个字节包含前一个读周期的转换结果代码。
在上电复位条件后,第一个字节读取为十六进制80。
最大A/D转换速率是由12c总线的实际速度给出的。
意思就是说,在开始读后,读的是上一次转换的结果!
流程为:IIC开始信号->地址读->等待PCF8591回应
->读PCF8591->主机回应->继续读->主机回应…->直到想停止AD转换了->不回应了->直接停止信号
从原理图可以看出,因为A0.A1.A2都连接到的是地,因此蓝桥杯板子上面PCF8591的地址为
写地址为:0x90
读地址为:0x91
然后
蓝桥杯板子上面PCF8591的4个ad通道分别连接到了4个不同的部分,其中省赛中考得最多的就是通道1和通道3,通道1连接到了光敏电阻,通道3连接到了滑动变阻器,而DA输出连接到的是板子上最右边的排针的19号引脚,这个在省赛也经常考到。下面就对这部分做说明了。
首先是对模式进行初始化,因此必须先写入控制直接
比如我们这里
这里为了方便使用了delay的形式,注意,平常不要使用这种方式
初始化函数为
#define ADCW 0x90
#define ADCR 0x91
bit b_AdcStart(uchar addr)
{
bit OK = 0;
IIC_Start();
IIC_SendByte(ADCW);
if(!IIC_WaitAck());
{
IIC_SendByte(addr);
if(!IIC_WaitAck());
{
IIC_Stop();
IIC_Start();
IIC_SendByte(ADCR);
if(!IIC_WaitAck())
{
OK = 1;
}
}
}
return OK;
}
读取函数为
uchar c_AdcRead()
{
uchar _data;
_data = IIC_RecByte();
IIC_SendAck(0);
return _data;
}
这里的步骤是严格按照步骤来的,必须首先写入控制字节才能够让pcf8591处于ad模式,并且在处于ad模式后,就可以一直开始读取数据,只要不停止,转换一次后,接收数据了,要主动回应pcf8591,让它进行下一次的ad转换!
注意: 第一次读取到的值一定是0x80,也就是十进制的128,这个数据是为了后面如果要用到自动递增通道,而给你一个起始值,让你在收到这个起始值后,你就能够知道当前数据开始从0通道开始了。
下面是应用程序
void main()
{
EA = 1;
UartInit();
b_AdcStart(0x03);
v_SL(LED, 0x1f);
while(1)
{
adcData = c_AdcRead();
Delay1000ms();
TI = 1;
printf("%bu\r\n", adcData);
}
}
void main()
{
EA = 1;
UartInit();
b_AdcStart(0x04);
v_SL(LED, 0x1f);
while(1)
{
adcData = c_AdcRead();
Delay1000ms();
TI = 1;
printf("channel %bu data is %bu\r\n", channel, adcData);
}
}
//channel是一个全局变量
uchar c_AdcRead()
{
uchar _data;
_data = IIC_RecByte();
IIC_SendAck(0);
if(_data == 128)
channel = -1;
else
channel = (channel + 1)%4;
return _data;
}
DA部分按照流程就是,首先写入控制字节将da功能开启,然后后面就是将需要的电压值,按照映射0对应0V,255对应5V来将值不断输入进去就是了。
一般比赛不是单独只考da,而是将ad和da连起来一起,因此这里给出两个一起用的写法
void AdcStart(uchar _addr, uchar _data)
{
uchar tmp;
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(_addr);
IIC_WaitAck();
IIC_SendByte(_data);
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0x91);
IIC_WaitAck();
tmp = IIC_RecByte();
}
void AdcStartRead()
{
IIC_SendAck(0);
val = IIC_RecByte()*100.0/51;
}
每次需要改变da的值的时候,就调用
b_AdcStart(0x43, 127);
其中后面的值就是da输出的值,而读取adc的值还是使用adcData = c_AdcRead();
不用变。
其实pcf8591的部分并没有多难,就只是在iic的基础上面加了一定的控制而已,因此必须要看懂英文手册,这样在进行比赛的时候,才能够游刃有余,这种东西也不需要背的。