打印
[经验分享]

PCF8591数模模数转换器

[复制链接]
38|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
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();        }}

使用特权

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

本版积分规则

49

主题

1335

帖子

0

粉丝