问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

PCF8591芯片的AD/DA转换详解(适用于蓝桥杯单片机)

创作时间:
作者:
@小白创作中心

PCF8591芯片的AD/DA转换详解(适用于蓝桥杯单片机)

引用
CSDN
1.
https://m.blog.csdn.net/wcl501375/article/details/129626609

PCF8591是一款常用的AD/DA转换芯片,广泛应用于各种单片机系统中。本文将详细介绍PCF8591的引脚功能、IIC通信协议以及AD/DA转换的具体实现方法,特别适合参加蓝桥杯单片机比赛的选手参考。

1. PCF8591的引脚介绍

在和单片机实际应用中它的接线图如下:


PCF8591有四个模拟信号输入端(模转数),一个输出端(数转模)。因为我们一般使用芯片的内部时钟,所以EXT和OSC接地,而VSS和VCC手册里写是用来上电复位的,只需要照着原理图接就行。

2. 用IIC发送或接收字节

发送的第一个字节

要用到IIC通信首先要发送的就是芯片的地址。高四位为PCF8591的专用地址,只要用这个芯片高四位就必须是1001,低四位中A0、A1、A2表示要选择那个PCF8591,比如前面的原理图A0、A1、A2全部接地所以它的地址是000,最后低位R/W表示下一步写入还是读出数据,0是写1是读 ,下一步还要写数据所以这一位置0,总结起来这一个字节就是0x90。

IIC_Start();//开始
IIC_SendByte(0x90);
IIC_WaitAck();//应答如果接收到的话这个函数会返回1

发送的第二个字节

这个字节用来控制芯片的功能:

  • 第一位和第二位:AD转换的输入通道选择位 00 >>AIN0, 01>>AIN1, 10>>AIN2, 11>>AIN3
  • 第三位:自动递增标志位,激活时给1,在每次A/D转换后,读取的通道会自动跳到下一位。
  • 第四位:无效位
  • 第五、六位:选择差分输入还是单端输入(如果对精度没有太大要求时选择单端输入置00)
  • 第七位: 使能模拟输出,使能时置1 ,表示切换为DA(数字转模拟模式)
  • 第八位:无效位

1. AD模式:比赛时建议一个一个通道使用,所以不启动递增位,发送字节为0x01(0000 001)接收通道1的电压值,这个时候所以要发送的控制字节已经发送完了,接下来是接收部分。

IIC_SendByte(0x01);
IIC_WaitAck();//应答标志位

2.DA模式:因为PCF8591只有一个DA输出口,只要给第七位置1,发送字节0x40 (0100 0000)。

IIC_SendByte(0x40);
IIC_WaitAck();

发送第三个字节

1. AD模式: 首先和发送字节时一样要发送对应地址,接收地址为0x90,而接收地址为0x91,然后接收代码。

IIC_Start();
IIC_SendByte(0x91);
IIC_WaitAck();
Date=IIC_RecByte();//要接收的值
IIC_SendAck(1);//如果接收到的话发送
IIC_Stop();//结束

2.DA模式: 发送最后一个控制输出值的代码。

IIC_WaitAck();				
IIC_SendByte(DATA); //要发送的值
IIC_WaitAck();				
IIC_Stop();	  

3. 发送和接收的字节转换

因为PCF8591检测的电压是以8位二进制发送给单片机的,所以单片机得到的值是0255,把它转换为05V的电压公式如下:

检测到的电压 = 单片机上电电压(5V)
(测量到的值
255)

发送时把电压数据转换为二进制对应值,与上面就是将05V转换为0255,把公式换一下即可。

发送要输出电压 = 255
(测量到的电压
单片机上电电压(5V))

4. 全部代码

IIC部分(官方的IIC代码SDA和 SCL可能是反的)

#include <REGX52.H>
#include "intrins.h"
#define DELAY_TIME 5
sbit SDA = P2^1;
sbit SCL = P2^0;
   //IIC延时
void IIC_Delay(unsigned char i)
{
  while(i--);        
}
//开始发送
void IIC_Start(void)
{
    SDA = 1;
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SDA = 0;
    IIC_Delay(DELAY_TIME);
    SCL = 0;	
}
//IIC结束发送
void IIC_Stop(void)
{
    SDA = 0;
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SDA = 1;
    IIC_Delay(DELAY_TIME);
}
//接收应答
void IIC_SendAck(bit ackbit)
{
    SCL = 0;
    SDA = ackbit;  					
    IIC_Delay(DELAY_TIME);
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SCL = 0; 
    SDA = 1;
    IIC_Delay(DELAY_TIME);
}
//发送应答
bit IIC_WaitAck(void)
{
    bit ackbit;
      SDA=1;
    SCL  = 1;
    IIC_Delay(DELAY_TIME);
    ackbit = SDA;
    SCL = 0;
    IIC_Delay(DELAY_TIME);
    return ackbit;
}
//发送一个字节
void IIC_SendByte(unsigned char byt)
{
    unsigned char i;
    for(i=0; i<8; i++)
    {
        SCL  = 0;
        IIC_Delay(DELAY_TIME);
        if(byt & 0x80) SDA  = 1;
        else SDA  = 0;
        IIC_Delay(DELAY_TIME);
        SCL = 1;
        byt <<= 1;
        IIC_Delay(DELAY_TIME);
    }
    SCL  = 0;  
}
//接收一个字节
unsigned char IIC_RecByte(void)
{
    unsigned char i, da;
    for(i=0; i<8; i++)
    {   
        SCL = 1;
    IIC_Delay(DELAY_TIME);
    da <<= 1;
    if(SDA) da |= 1;
    SCL = 0;
    IIC_Delay(DELAY_TIME);
    }
    return da;    
}

接收电压数据

float PCF8951_Read(unsigned char date)
{
    float Date;
    IIC_Start();//开始
    IIC_SendByte(0x90);//发送地址PCF8591的地址,表示要对其发送数据
    IIC_WaitAck();    //芯片接收到要返回一个应答位
    IIC_SendByte(date);//发送要执行的命令
    IIC_WaitAck();
    
    IIC_Start();
    IIC_SendByte(0x91);//发送地址PCF8591的地址,表示要接收它的数据
    IIC_WaitAck();
    Date=IIC_RecByte();//接收一个字节
    IIC_SendAck(1);//接收完成之后要发送一个1
    IIC_Stop();
    
    return (Date/255)*5;//电平转换
}

发送电压数据

void PCF8951_Out(float DATE)
{
    IIC_Start();
    IIC_SendByte(0x90);
    IIC_WaitAck();
    IIC_SendByte(0x40);
    IIC_WaitAck();
    IIC_WaitAck(255*(DATE/5));//发送要输出的电压
    IIC_WaitAck();
    IIC_Stop();
}

为什么接收数据的代码那么长,而发送数据的代码那么少呢?因为在接收电压数据的时候 IIC_SendByte(0x90)是向芯片写数据,所以加IIC_SendByte(0x91)告诉芯片给我发某一个地址的数据。而我们给芯片发送数据时,芯片只有一个输出DA值的地址,不需要告诉它写入什么地址。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号