PCF8951(AD-DA)

AD-DA芯片基本知识

ADC:模拟信号转化为数字信号,特定电压转换为二进制

DAC:数字信号转化为模拟信号,通过二进制让芯片输出特定的电压

电路原理

AIN:电路输入引脚,可外界电压进行采集

A:器件地址

AOUT:DA输出口,输出电压

vref:参考电压

RD1:光敏电阻,光照越强,电阻越低;光照越弱,电阻越高(但有分压电阻,所以光照越弱,AIN1口 的电压越低,光照越强,AIN1口的电压越高)

Rb2:旋转电位器,最低0V,最高5V

器件地址:1001 000(R/W):0x90代表51向PCF8591写数据。0x91代表51读PCF8591的数据

PS:AIN2接的是差分放大电路,是需要接外接电源的,不考

AT24C02器件地址:1010 000(R/W):0xa0代表51向AT24C02写数据。0xa0代表51读AT24C02的数据

芯片特性

  1. 单电源供电

  1. 工作电压2.5V~6V

  1. 低待机电流

  1. IIC通讯接口

  1. 器件地址可以定义(A0~A2)

  1. 4路模拟量输入口(AD),可以单端输入,也可以差分输入

  1. 通道选择可以自动增量

  1. 模拟输入范围是Vss~Vdd,蓝桥杯板载0V~5V

  1. 片内跟踪保持电路

  1. 8位逐次逼近型AD转换器

  1. 模拟输出的乘法DAC

ADC采样编程

类似DS18B20,发送相信的控制数据,让器件干嘛干嘛

PCF8591控制字

采样通道0控制字:0100 0000(0x40)

采样通道1(光敏电阻)控制字:0100 0001(0x41)

采样通道3(旋转电位器)控制字:0100 0003(0x43)

类似EEPROM,51向PCF8591写控制字(选择通道),51读PCF8591发来的数据

//读取PCF8591
u8 ucRead_ADC(u8 ctrl_byte) //ctrl_byte:通道选择控制字
{
    u8 adc_val; 
    IIC_Start();
    IIC_SendByte(0x90);
    IIC_WaitAck();
    IIC_SendByte(ctrl_byte);
    IIC_WaitAck();
    
    IIC_Start();
    IIC_SendByte(0x91);
    IIC_WaitAck();
    adc_val=IIC_RecByte();
    IIC_SendAck(1);
    IIC_Stop();    
    
    return adc_val;
}
//利用PCF8591采集不同通道的电压

#include "system.h"
HexToBin led_ctrl,uln_ctrl;

void vSystem_Init(void)
{
    vDevice_Ctrl(0xa0,0);                 /* 关闭蜂鸣器、继电器*/
    led_ctrl.hex=0xff;
    vDevice_Ctrl(0x80,led_ctrl.hex);     /* 关闭LED*/
}
//ADC采样
u8 cnt_adc;
u8 ch0,ch1,ch3;
void vADC_Process()
{
    if(cnt_adc>=10)
    {
        cnt_adc=0;
        EA = 0;                    /*关闭中断,中断会影响IIC的通讯,所以数码管显示会闪烁
                                   关闭中断来避免这种影响是不太好的,更好的方法请看下一节*/

        ch1 = ucRead_ADC(0x41);    /*读取ADC转换结果耗时0.75ms ,所以不要放在中断里面
                                   中断讲究快进快出*/
        EA = 1;
    }
}


//数码管操作函数
void vSMG_Process()
{
    smg_buf[0]=smg_code[ch1/100];//显示的数据是0~255,要想显示电压值还要再除以51
    smg_buf[1]=smg_code[ch1/10%10];//因为0~255与0~5有51的一个倍数
    smg_buf[2]=smg_code[ch1%10];
    smg_buf[3]=0x00;
    smg_buf[4]=0x00;
    smg_buf[5]=0x00;
    smg_buf[6]=0x00;
    smg_buf[7]=0x00;
}

void main(void)
{
    vSystem_Init();
    vTimer2_Init();
    while(1)
    {
        vSMG_Process();
        vADC_Process();
    }
}


//中断服务程序
void vTimer2_ISR() interrupt 12         //中断入口
{
    cnt_adc++;
    vSMG_Display();
}

中断干扰IIC通讯

原因:中断函数里对P2口进行了字节赋值,导致IIC时序出错。中断函数里执行的是smg_display(), 里面有对P2的整体赋值,导致P20、P21拉低

解决办法:对锁存器-译码器进行操作时,只改变P27 P26 P25的值。修改锁存器-译码器控制函数如下

void vDevice_Ctrl(unsigned char p2data,unsigned char p0data)
{
    P0 = p0data;
    P2 = P2 & 0x1f | p2data;
    P2 = P2 & 0x1f;
}

读取通道相反问题

有时候同时读取两个通道的电压,但是读取出来的通道电压是相反的。只读取一个通道的值是没有问题的

原因:每次A/D转换读到的数值都是上一次转换的结果

解决办法:直接读两次。读第二次再进行变量赋值。当然也可以直接把变量赋值对换啦,但是这样很容 易把自己搞晕😵

//利用PCF8591采集不同通道的电压

#include "system.h"
HexToBin led_ctrl,uln_ctrl;

void vSystem_Init(void)
{
    vDevice_Ctrl(0xa0,0);                 /* 关闭蜂鸣器、继电器*/
    led_ctrl.hex=0xff;
    vDevice_Ctrl(0x80,led_ctrl.hex);     /* 关闭LED*/
}
//ADC采样
u8 cnt_adc;
u8 ch0,ch1,ch3;
void vADC_Process()
{
    if(cnt_adc>=10)
    {
        cnt_adc=0;
  
        ucRead_ADC(0x41); 
        ch1 = ucRead_ADC(0x41);   
        ucRead_ADC(0x43); 
        ch3 = ucRead_ADC(0x43); 
    }
}


//数码管操作函数
void vSMG_Process()
{
    smg_buf[0]=smg_code[ch1/100];//显示的数据是0~255,要想显示电压值还要再除以51
    smg_buf[1]=smg_code[ch1/10%10];//因为0~255与0~5有51的一个倍数
    smg_buf[2]=smg_code[ch1%10];
    smg_buf[3]=0x00;
    smg_buf[4]=0x00;
    smg_buf[5]=0x00;
    smg_buf[6]=0x00;
    smg_buf[7]=0x00;
}

void main(void)
{
    vSystem_Init();
    vTimer2_Init();
    while(1)
    {
        vSMG_Process();
        vADC_Process();
    }
}


//中断服务程序
void vTimer2_ISR() interrupt 12         //中断入口
{
    cnt_adc++;
    vSMG_Display();
}

ADC值的转换

由于PCF8591是一个8位的ADC,所以读取到的值范围是0~255,但是比赛一般不会要求直接返回0~255的数据,一般要求返回的是电压,所以还需要进行数据的转换。

  1. 将AD值(0~255)转换为实际电压(0~5),先成100,再除以51。先乘100的原因是把直接除51时的小数转换成整数,方便在数码管上显示。

  1. 将AD值转换为0~99的数值,直接除2.57

  1. 将AD值转换成区间范围,比如1~51->1 51~102->2.........

//ADC采样
u8 cnt_adc;
u8 ch0,ch1,ch3;
u16 ch3_volt;
u8 ch3_0_99,ch3_1_5;
void vADC_Process()
{
    if(cnt_adc>=10)
    {
        cnt_adc=0;
        ucRead_ADC(0x41);
        ch1 = ucRead_ADC(0x41);            //光敏电阻的AD值 - 通道1
        ucRead_ADC(0x43);
        ch3 = ucRead_ADC(0x43);            //旋转电位器的AD值 - 通道3
        
        ch3_volt = ch3*100/51;            // 0~255 --> 0~500
        ch3_0_99 = ch3/2.57f;            // 0~255 --> 0~99
        ch3_1_5 = ch3/51.1f + 1;        //区间范围,除51.1是为了防止255显示区间范围6
    }
}

DAC输出电压

//输出DAC
void vWrite_DAC(u8 dat)
{
    IIC_Start();
    IIC_SendByte(0x90);
    IIC_WaitAck();

    IIC_SendByte(0x40);//通道选择无所谓,只要前面是0x4,启用DAC
    IIC_WaitAck();

    IIC_SendByte(dat); 
    IIC_WaitAck();
    IIC_Stop();
}

主函数

#include "system.h"
HexToBin led_ctrl,uln_ctrl;


void vSystem_Init(void)
{
    vDevice_Ctrl(0xa0,0);                 /* 关闭蜂鸣器、继电器*/
    led_ctrl.hex=0xff;
    vDevice_Ctrl(0x80,led_ctrl.hex);     /* 关闭LED*/
}
//ADC采样
u8 cnt_adc;
u8 ch0,ch1,ch3;
u16 ch3_volt;
u8 ch3_0_99,ch3_1_5;
void vADC_Process()
{
    if(cnt_adc>=10)
    {
        cnt_adc=0;
        ucRead_ADC(0x41);
        ch1 = ucRead_ADC(0x41);            //光敏电阻的AD值 - 通道1
        ucRead_ADC(0x43);
        ch3 = ucRead_ADC(0x43);            //旋转电位器的AD值 - 通道3
        
        ch3_volt = ch3*100/51;            // 0~255 --> 0~500
        ch3_0_99 = ch3/2.57f;            // 0~255 --> 0~99
        ch3_1_5 = ch3/51.1f + 1;
    }
}

//DAC输出
u8 cnt_dac;
void vDAC_Process()
{
    static u8 dac_val=0;
    if(cnt_dac>=2)
    {
        cnt_dac=0;
        vWrite_DAC(dac_val++);
    }
}

//数码管操作函数
void vSMG_Process()
{
    smg_buf[0]=smg_code[ch3_volt/100]|0x80;            //旋转电位器的电压值
    smg_buf[1]=smg_code[ch3_volt/10%10];    
    smg_buf[2]=smg_code[ch3_volt%10];
    smg_buf[3]=0x00;
    smg_buf[4]=smg_code[ch3_0_99/10];
    smg_buf[5]=smg_code[ch3_0_99%10];        
    smg_buf[6]=0x00;
    smg_buf[7]=smg_code[ch3_1_5];
}

void main(void)
{
    vSystem_Init();
    vTimer2_Init();
    while(1)
    {
        vSMG_Process();
        vADC_Process();
        vDAC_Process();
    }
}


//中断服务程序
void vTimer2_ISR() interrupt 12         //中断入口
{
    cnt_adc++;
    cnt_dac++;
    vSMG_Display();
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值