单片机PCF8591信号传输详解

PCF8591 是一款结合了模拟到数字转换器(A/D)和数字到模拟转换器(D/A)的芯片,广泛应用于各种需要模拟与数字信号交互的系统。其具有多通道模拟输入、模拟输出以及灵活的控制字节配置功能,非常适合在自动化控制、传感器接口等领域中使用。本文将从多个方面介绍 PCF8591 的工作原理、功能以及应用。

视频讲解:
51单片机入门教程-2020版 程序全程纯手打 从零开始入门

PCF8591介绍

PCF8591是八位的A/D和D/A转换器,通过I2C总线串行输入/输出,通过3个硬件地址(A0、A1、A2)引脚编址,具有4个模拟输入(AIN0~AIN3)通道,1个输出通道(AOUR:D/A转换输出端)和1个串行I2C总线接口。

I²C 总线接口

PCF8591 通过 I²C 总线进行与外部设备的通信。I²C 是一种常见的串行通信协议,由两根信号线组成:SCL(时钟线)和 SDA(数据线)。除此之外,PCF8591 还通过 A0、A1、A2 引脚设置硬件地址,方便与其他 I²C 设备进行区分。

主要寄存器功能

PCF8591 中有多个重要寄存器来控制其功能:

  • 状态寄存器(STATUS REGISTER):存储芯片的状态信息。
  • DAC 数据寄存器(DAC DATA REGISTER):存储 D/A 转换的数据。
  • ADC 数据寄存器(ADC DATA REGISTER):存储 A/D 转换后的数字数据。
  • 电源和复位

    PCF8591 由 VDD 和 VSS 提供电源,且芯片上电时会自动进行复位操作,以确保其在正确的状态下开始工作。

    振荡器

    PCF8591 内置了一个振荡器,OSC 引脚可以接入外部时钟信号,或者使用芯片内部的振荡器生成时钟信号,为芯片提供所需的工作时钟。

    控制逻辑

    PCF8591 内部有控制逻辑模块,根据状态寄存器的内容和控制字节的设置,控制芯片的工作模式和操作流程。

    模拟多路复用器(Analog Multiplexer)

    该芯片配备了一个模拟多路复用器,支持将四个模拟输入通道(AIN0 – AIN3)进行选择。用户可以根据需要将这些输入配置为单端或差分输入模式,适应不同的应用需求。

    采样保持电路(Sample and Hold)

    采样保持电路用于采样输入的模拟信号,并将其保持一段时间,以便进行 A/D 转换。这一过程确保了模拟信号在转换过程中的稳定性。

    比较器(Comparator)

    PCF8591 内部集成了一个比较器,用于将采样保持电路中的模拟信号与 DAC 输出的信号进行比较,支持逐次逼近算法的实现。这一过程是 A/D 转换的关键部分。

    逐次逼近寄存器 / 逻辑(SAR Logic)

    PCF8591 使用逐次逼近算法(Successive Approximation)进行 A/D 转换。通过逐步逼近的方法,模拟信号会被转化为相应的数字信号,并存储在 ADC 数据寄存器中。

    D/A 转换器(DAC)

    PCF8591 的 D/A 转换器将存储在 DAC 数据寄存器中的数字信号转化为模拟电压信号,并通过 AOUT 引脚输出。参考电压(VREF)和模拟地(AGND)为 D/A 转换提供了基准电压。

    控制字节的结构

    控制字节由 8 位组成,最高位(MSB)和次高位固定为 0,剩余的 6 位用于配置芯片的不同功能。控制字节中的具体位控制如下:

    1. 模拟输入编程(ANALOGUE INPUT PROGRAMMING):
  • 00:配置为四个单端输入
  • 01:配置为三个差分输入
  • 10:单端和差分混合输入
  • 11:配置为两个差分输入
    1. A/D 通道号(A/D CHANNEL NUMBER)
      由控制字节低四位中的低两位来决定,分别对应选择通道 0、通道 1、通道 2、通道 3。
      通道1为光敏电阻,通道3为滑动电阻。

    2. 自动递增标志(AUTO-INCREMENT FLAG)
      如果该位为 1,则激活自动递增功能,A/D 转换完成后,通道号会自动递增。

    3. 模拟输出使能标志(ANALOGUE OUTPUT ENABLE FLAG)
      若该位为 1,则激活模拟输出功能。

    一般来说,使用AD时的配置为光敏电阻:0x01 ,滑动电阻:0x03,
    DA:0x41。

    功能描述

  • 在 I²C 总线系统中,每一个 PCF8591 设备都需通过向其发送有效地址来激活。该地址由固定部分和可编程部分构成。可编程部分必须依据地址引脚 A0、A1 和 A2 进行设置。在 I²C 总线协议里,地址总是要在起始条件之后作为第一个字节发送出去。地址字节的最后一位是读 / 写位,它决定了后续数据传输的方向

  • 地址发送规范:I²C 总线有着严格的通信协议规范,在数据传输流程中,当主设备发起与 PCF8591 设备的通信时,首先会发送一个起始条件信号(SDA 线由高电平跳变为低电平,同时 SCL 线保持高电平)。在起始条件信号之后,主设备紧接着要发送的第一个字节就是目标 PCF8591 设备的地址,这种规范保证了总线上所有设备能准确识别主设备的通信意图。

  • 读 / 写位功能:地址字节的最后一位起着关键作用,它被称为读 / 写位。当这个读 / 写位被设置为 0 时,就表示后续的数据传输方向是主设备向 PCF8591 设备写入数据;反之,若读 / 写位为 1,则意味着后续主设备要从 PCF8591 设备读取数据。例如,当主设备需要获取 PCF8591 的 A/D 转换结果时,发送的地址字节中的读 / 写位会被置为 1;而当主设备要配置 PCF8591 的控制字节时,读 / 写位则会被设置为 0。

  • 代码实现

    创建iic.c和iic.h

    iic.h

    #ifndef __iic_H__
    #define __iic_H__
    
    unsigned char AD_Read();
    void unsigned char DA_Write(unsigned char data);
    
    #endif
    

    iic.c

    导入官方给的iic底层通信协议代码,在最后添加函数unsigned char AD_Read()和
    void unsigned char DA_Write(unsigned char data)。

    #include <STC15F2K60S2.H>
    #include <intrins.h>
    /*	#   I2C代码片段说明
    	1. 	本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
    	2. 	参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
    		中对单片机时钟频率的要求,进行代码调试和修改。
    */
    
    #define DELAY_TIME	5
    sbit scl = P2^0;
    sbit sda = P2^1;
    //
    static void I2C_Delay(unsigned char n)
    {
        do
        {
            _nop_();_nop_();_nop_();_nop_();_nop_();
            _nop_();_nop_();_nop_();_nop_();_nop_();
            _nop_();_nop_();_nop_();_nop_();_nop_();		
        }
        while(n--);      	
    }
    
    //
    void I2CStart(void)
    {
        sda = 1;
        scl = 1;
    	I2C_Delay(DELAY_TIME);
        sda = 0;
    	I2C_Delay(DELAY_TIME);
        scl = 0;    
    }
    
    //
    void I2CStop(void)
    {
        sda = 0;
        scl = 1;
    	I2C_Delay(DELAY_TIME);
        sda = 1;
    	I2C_Delay(DELAY_TIME);
    }
    
    //
    void I2CSendByte(unsigned char byt)
    {
        unsigned char i;
    	
        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);
        }
    	
        scl = 0;  
    }
    
    //
    unsigned char I2CReceiveByte(void)
    {
    	unsigned char da;
    	unsigned char i;
    	for(i=0;i<8;i++){   
    		scl = 1;
    		I2C_Delay(DELAY_TIME);
    		da <<= 1;
    		if(sda) 
    			da |= 0x01;
    		scl = 0;
    		I2C_Delay(DELAY_TIME);
    	}
    	return da;    
    }
    
    //
    unsigned char I2CWaitAck(void)
    {
    	unsigned char ackbit;
    	
        scl = 1;
    	I2C_Delay(DELAY_TIME);
        ackbit = sda; 
        scl = 0;
    	I2C_Delay(DELAY_TIME);
    	
    	return ackbit;
    }
    
    //
    void I2CSendAck(unsigned char ackbit)
    {
        scl = 0;
        sda = ackbit; 
    	I2C_Delay(DELAY_TIME);
        scl = 1;
    	I2C_Delay(DELAY_TIME);
        scl = 0; 
    	sda = 1;
    	I2C_Delay(DELAY_TIME);
    }
    unsigned char AD_Read()
    {
        unsigned char temp; // 声明一个变量来存储返回的值
        
        I2CStart(); // 开始 I²C 通信
        I2CSendByte(0x90); // 发送字节 0x90 (设备地址 + 写操作)
        I2CWaitAck(); // 等待设备的 ACK 确认
        
        I2CSendByte(0x01); // 选择通道1即光敏电阻
        //I2CSendByte(0x03); // 选择通道3即滑动电阻
        I2CWaitAck(); // 等待设备的 ACK 确认
        
        I2CStart(); // 再次开始 I²C 通信
    	I2CSendByte(0x91); // 发送字节 0x91 (设备地址 + 读操作)
        I2CWaitAck(); // 等待设备的 ACK 确认
        
        temp = I2CReceiveByte(); // 接收一个字节数据
        I2CSendAck(1); // 发送 ACK 信号
        I2CStop(); // 结束 I²C 通信
        
        return temp; // 返回读取的字节数据
    }
    
    void unsigned char DA_Write(unsigned char data)
    {
    	I2CStart();
    	I2CSendByte(0x90);
    	I2CWaitAck();
    	I2CSendByte(0x41);//数字转模拟量
    	I2CWaitAck();
    	I2CSendByte(data);
    	I2CWaitAck();
    	
    	I2CStop();
    }
    
    

    作者:星光始终闪耀

    物联沃分享整理
    物联沃-IOTWORD物联网 » 单片机PCF8591信号传输详解

    发表回复