打印
[经验分享]

PCF8591数模模数转换器

[复制链接]
4493|62
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
benjaminka|  楼主 | 2024-12-20 04:25 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
0.本节任务
        了解PCF8591的使用方法,并利用PCF8591检测模拟电压信号(模数转换)。
1.工作原理
PCF8591简介
PCF8591的引脚和原理图连接
        PCF8591是一种单芯片、单电源、低功耗的8位COMS数据采集器件,具有4个模拟输入、1个模拟输出和IIC总线。
PCF8591引脚说明
   AINO~3是四个模拟输入,VSS为负电源,VDD为正电源,VREF为参考电压(蓝桥杯平台接VCC),EXT是内部/外部时钟的切换开关(为1时允许从OSC输入时钟信号,但是蓝桥杯平台接地了),OSC是外部时钟信号的输入端,A0~A2为IIC相关的硬件地址(蓝桥杯平台接地),AGND为模拟地(蓝桥杯平台接地),SCL和SDA为IIC的信号线。
蓝桥杯单片机平台的原理图
        IIC内容详见,本节不再做介绍:



PCF8591的设备地址
        PCF8591的设备地址如下图所示,根据蓝桥杯给出原理图连接,写地址为0x90(1001 0000),读地址为0x91(1001 0001)
PCF8951的控制字节       
        PCF8951的控制字节如下图所示:
        第0、1位控制AD转换的信道,可以选择四个端口的作为模拟信号的输入端,00为信道0,01为信道1,10为信道2,11为信道3;
        第3位是自动增量标志位,若置1,每次检测完一个信道后会自动检测下一个信道;
        第4位恒为0;
        第5、6位控制模拟输入的模式,不同模式有不同数量的信道,一般用单端输入(00)较多;
        第7位控制模拟信号输出/输入,AD转换置0,DA转换置1(有资料写只有DA转换必须置1,AD转换置0,1均可);
        第8位恒为0;
        蓝桥杯单片机中AIN3与Rb2可调电阻相连,所以一般可以使用0000 0011(0x03)(AD转换,单端输入,AIN3作为模拟输入的信道)作为控制字发送
开始AD转换
        通过datasheet和相关资料,PCF8951的开始AD转换分两步:        ①.IIC开始->写PCF8951(0x90)->写控制字->IIC停止(第一步datasheet中似乎没有明确写出,它只说了DA转换需要写控制字???)        ②.IIC开始->读PCF8951->PCF8951发送数据...->IIC停止        值得注意的是,第二部开始读PCF8951后,其发送的第一个数据是上一次转换的数据,如果是刚刚上电,则第一个数据是0x80.
datasheet中给出的A/D转换示意
        AD转换单端输入特性如下图所示:
        根据之前的原理图连接,VREF = VCC(5v),VAGND= 0,就可以算出Vlsb约等于0.0195v,假设模拟输入VAIN为3V,则对应横坐标属于154这个阶梯上,此时PCF8951发送的数据为0x9A.
2.程序设计        设定PCF8591为AD转换模式,并通过AIN3检测Rb2可变电阻上电压。        IIC驱动代码仍是官方给出的代码,我们只需要控制好读写地址和控制字就行:#include "reg52.h"#include "intrins.h"#define DELAY_TIME 5/** 定义I2C总线时钟线和数据线 */sbit scl = P2^0;sbit sda = P2^1;/*** @brief I2C总线中一些必要的延时** @param[in] i - 延时时间调整.* @return none*/void i2c_delay(unsigned char i){    do    {        _nop_();_nop_();_nop_();_nop_();_nop_();        _nop_();_nop_();_nop_();_nop_();_nop_();        _nop_();_nop_();_nop_();_nop_();_nop_();                    }    while(i--);        }/*** @brief 产生I2C总线启动条件.** @param[in] none* @param[out] none* @return none*/void i2c_start(void){    sda = 1;    scl = 1;    i2c_delay(DELAY_TIME);    sda = 0;    i2c_delay(DELAY_TIME);    scl = 0;    }/*** @brief 产生I2C总线停止条件** @param[in] none* @param[out] none.* @return none*/void i2c_stop(void){    sda = 0;    scl = 1;    i2c_delay(DELAY_TIME);    sda = 1;    i2c_delay(DELAY_TIME);       }/*** @brief I2C发送一个字节的数据** @param[in] byt - 待发送的字节* @return none*/void i2c_sendbyte(unsigned char byt){    unsigned char i;//        EA = 0;   //关闭中断,避免因为中断而影响总写读写的时序,导致读写失败。    for(i=0; i<8; i++){        scl = 0;        i2c_delay(DELAY_TIME);        if(byt & 0x80){            sda = 1;        }        else{            sda = 0;        }        i2c_delay(DELAY_TIME);        scl = 1;        byt <<= 1;        i2c_delay(DELAY_TIME);    }        EA = 1;//    scl = 0;  }/*** @brief 等待应答** @param[in] none* @param[out] none* @return none*/unsigned char i2c_waitack(void){        unsigned char ackbit;            scl = 1;    i2c_delay(DELAY_TIME);    ackbit = sda; //while(sda);  //wait ack    scl = 0;    i2c_delay(DELAY_TIME);                return ackbit;}/*** @brief I2C接收一个字节数据** @param[in] none* @param[out] da* @return da - 从I2C总线上接收到得数据*/unsigned char i2c_receivebyte(void){        unsigned char da;        unsigned char i;//        EA = 0;                for(i=0;i<8;i++){                   scl = 1;                i2c_delay(DELAY_TIME);                da <<= 1;                if(sda)                         da |= 0x01;                scl = 0;                i2c_delay(DELAY_TIME);        }        EA = 1;//        return da;    }/*** @brief 发送应答** @param[in] ackbit - 设定是否发送应答* @return - none*/void i2c_sendack(unsigned char ackbit){    scl = 0;    sda = ackbit;  //0:发送应答信号;1:发送非应答信号    i2c_delay(DELAY_TIME);    scl = 1;    i2c_delay(DELAY_TIME);    scl = 0;         sda = 1;    i2c_delay(DELAY_TIME);}/*** @brief 读写操作过程中一些必要的延时** @param[in] i - 指定延时时间* @return - none*/void operate_delay(unsigned char t){        unsigned char i;                while(t--){                for(i=0; i<112; i++);        }}        初始化和读PCF8591:#include "PCF8951_AD.h"#include "STC15F2K60S2.H" #include "intrins.h"#include "HC138.h"#include "LedBuzzerRelay.h"#include "SMG.h"#include "i2c.h"//初始化PCF8951,AD转换,单端输入,AIN3作为模拟输入信道void PCF8951Init(void){        i2c_start();  i2c_sendbyte(0x90);//写地址  i2c_waitack();  i2c_sendbyte(0x03);  i2c_waitack();  i2c_stop();        operate_delay(10);}        unsigned char ReadPCF8951(void){        unsigned char da;        i2c_start();        i2c_sendbyte(0x91);//读地址        i2c_waitack();                da = i2c_receivebyte();        i2c_sendack(1);        i2c_stop();        return da;}                主函数(这里只对读出来的数据显示了X上坐标,可以乘上分辨率得出实际电压):#include "STC15F2K60S2.H" #include "intrins.h"#include "HC138.h"#include "LedBuzzerRelay.h"#include "SMG.h"#include "i2c.h"#include "PCF8951_AD.h"void main(void){                unsigned char dat;        AllOff();        PCF8951Init();        //LEDOn();        while(1){                        dat = ReadPCF8951();                DisplaySMG(0,dat/100);                DelaySMG();                DisplaySMG(1,(dat%100)/10);                DelaySMG();                DisplaySMG(2,dat%10);                DelaySMG();        }}

使用特权

评论回复
沙发
lihuami| | 2025-1-3 11:33 | 只看该作者
在模拟信号输入和输出端添加适当的滤波电路,以减少噪声对转换精度的影响。

使用特权

评论回复
板凳
earlmax| | 2025-1-3 11:47 | 只看该作者
PCF8591 的工作电压范围为 2.5V-6V,要确保提供的电源电压在这个范围内,以保证芯片正常工作。如果电压过低,可能导致芯片无法正常启动或工作不稳定;电压过高则可能损坏芯片

使用特权

评论回复
地板
plsbackup| | 2025-1-3 12:07 | 只看该作者
PCF8591 采用 I2C 通信协议,要确保主控芯片的 I2C 接口配置正确,包括时钟频率、数据格式等。PCF8591 的最大转换速率由 I2C 总线的最大速率决定,因此要根据实际需求合理设置 I2C 总线的时钟频率,不过过高的频率可能会导致通信不稳定

使用特权

评论回复
5
robincotton| | 2025-1-3 13:20 | 只看该作者
根据A0~A2(或A0~A3)引脚的设置,确定设备的I2C地址。例如,如果A0~A2都接地,则设备地址为0x90(写)和0x91(读)。

使用特权

评论回复
6
lzbf| | 2025-1-3 13:42 | 只看该作者
PCF8591的地址引脚A0、A1和A2可用于硬件地址编程,允许在同一个I2C总线上接入多个PCF8591器件。确保正确配置地址,以避免通信冲突

使用特权

评论回复
7
modesty3jonah| | 2025-1-3 14:16 | 只看该作者
PCF8591作为I2C总线上的从站,需要与主站(如Arduino或树莓派)进行通信。主站负责启动通信,发送从站地址和读/写位,以及发送或接收数据。

使用特权

评论回复
8
wilhelmina2| | 2025-1-3 14:53 | 只看该作者
根据需要选择合适的模拟输入通道,并通过跳线帽或其他方式将模拟信号连接到相应的输入引脚上。

使用特权

评论回复
9
elsaflower| | 2025-1-3 15:34 | 只看该作者
PCF8591的工作电压范围是2.5V到6V。确保为其提供合适的电源电压,以免损坏器件。

使用特权

评论回复
10
uytyu| | 2025-1-3 16:42 | 只看该作者
PCF8591的ADC和DAC转换精度受参考电压的影响

使用特权

评论回复
11
maudlu| | 2025-1-3 17:14 | 只看该作者
尽量远离强磁场源,并对敏感线路(如模拟输入线)采用屏蔽措施,以减少外界干扰对转换结果的影响。

使用特权

评论回复
12
ccook11| | 2025-1-4 07:54 | 只看该作者
PCF8591有三个地址引脚A0、A1和A2,可以通过这些引脚设置不同的I2C地址10。确保在多器件系统中正确设置每个PCF8591的地址,以避免地址冲突。

使用特权

评论回复
13
linfelix| | 2025-1-4 08:07 | 只看该作者
由于 PCF8591 是模拟与数字混合的芯片,容易受到外部干扰。在布线时,要将模拟信号线路和数字信号线路分开布局,避免相互干扰。同时,尽量缩短信号传输线的长度,减少信号在传输过程中受到的干扰。

使用特权

评论回复
14
kkzz| | 2025-1-5 09:26 | 只看该作者
根据需要编写代码来读取或写入PCF8591的数据。对于读取操作,需要先发送从站地址和读位,然后接收数据;对于写入操作,需要先发送从站地址和写位,然后发送数据。

使用特权

评论回复
15
uptown| | 2025-1-5 10:25 | 只看该作者
PCF8591的工作电压范围通常是+2.7V到+5.5V。确保提供的电源电压在这个范围内,并且电源足够稳定,因为不稳定的电源可能会导致转换结果的误差。

使用特权

评论回复
16
pentruman| | 2025-1-5 10:58 | 只看该作者
根据主控制器的I2C总线速率和PCF8591的规格要求,选择合适的通信速率。

使用特权

评论回复
17
louliana| | 2025-1-5 12:23 | 只看该作者
PCF8591的性能也会受到环境温度的影响,特别是在极端条件下。了解器件在不同温度下的特性,并在必要时采取补偿措施。

使用特权

评论回复
18
bartonalfred| | 2025-1-5 12:45 | 只看该作者
根据数据手册中的时序图正确编写读取/写入命令序列。例如,要从某个通道读取ADC值,必须先发送一个控制字节指定通道号及其它参数,然后才能开始读取转换结果。

使用特权

评论回复
19
hudi008| | 2025-1-5 13:09 | 只看该作者
正确设置VREF参考电压,以确保模拟输入和输出的准确性。

使用特权

评论回复
20
maudlu| | 2025-1-5 14:22 | 只看该作者
主站向PCF8591发送的第一个字节是控制字节,用于设定PCF8591的模拟输出参数。控制字节的高半字节用于允许模拟输出和设置模拟输入形式(单端或差分),低半字节用于选择模拟输入量。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

61

主题

1402

帖子

0

粉丝