打印
[经验分享]

PCF8591数模模数转换器

[复制链接]
865|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
uytyu|  楼主 | 2024-10-15 22:00 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
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();
        }
}


使用特权

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

本版积分规则

14

主题

3217

帖子

1

粉丝