【蓝桥杯单片机】六、 PCF8591- DAC和DAC

1、DACDAC的基本原理

2、PCF8591的基本介绍

它的功能包括多路模拟输入、内置跟踪保持、8-bit 模数转换和 8-bit 数模转换。PCF8591的最大转化速率由 I2C 总线的最大速率决定

2.1 、 器件总地址

PCF8591 采用典型的 I2C 总线接口器件寻址方法,即总线地址由器件地址引脚地址方向位组成。飞利蒲公司规定 A/D 器件地址为 1001。引脚地址为 A2A1A0,其值由用户选择,因此 I2C 系统中最多可接 23=8 个具有 I2C 总线接口的 A/D 器件。地址的最后一位为方向位 R/ w ,当主控器对A/D 器件进行读操作时为 1进行写操作时为 0。总线操作时,由器件地址、引脚地址和方向位组成的从地址为主控器发送的第一字节。
在这里插入图片描述
在这里插入图片描述

2.2 、 控制字节

控制字节用于实现器件的各种功能,如模拟信号由哪几个通道输入等。控制字节存放在控制寄存器中。总线操作时为主控器发送的第二字节。其格式如下所示:

其中:
D1、D0 两位是 A/D 通道编号:00 通道 0,01 通道 1,10 通道 2,11 通道 3
D2 自动增益选择(有效位为 1)
D5、D4 模拟量输入选择:00 为四路单数入、01 为三路差分输入、10 为单端与差分配合输入、11 为模拟输出允许有效
D6:模拟输出允许(1有效)
在这里插入图片描述

2.3、IIC通信

I2C总线是不同的IC或模块之间的双向两线通信。这两条线是串行数据线(SDA)和串行时钟线(SCL)。这两条线必须通过上拉电路连接至正电源。数据传输只能在总线不忙时启动

3.1.1、数据信息

一个数据位在每一个时钟脉冲期间传输。SDA线上的数据必须在时钟脉冲的高电压期间保持稳定这个期间数据线上的改变将被当作控制信号在这里插入图片描述
发送一个字节:

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;    
}

3.1.2、开始信号和停止信号

数据和时钟线在总不忙时保持高电平。在时钟为高电平时,数据线上的一个由高到低的变化被定义为开始条件。时钟为高电平时,数据线上的一个由低到高的变化被定义为停止条件。
在这里插入图片描述
开始

void IIC_Start(void)
{
//起始同时为高电平
    SDA = 1;
    SCL = 1;
    //一段时间后
    IIC_Delay(DELAY_TIME);
    //拉低SDA
    SDA = 0;
    //再等待一段时间按
    IIC_Delay(DELAY_TIME);
    //拉低SCL
    SCL = 0;	
}

停止

void IIC_Stop(void)
{
	//其实SDA为低,SCL为高
    SDA = 0;
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SDA = 1;
    IIC_Delay(DELAY_TIME);
}

3.1.3、应答

在开始和停止条件之间从发送机传输到接收机的数据字节数是没有限制的。每个8位数据字节之后紧跟着一个应答位。应答位是由发送机放在总线的一个高电平,而主机也产生一个额外的与应答有关的时钟脉冲地址匹配的从接收机必须在接收每个字节后产生一个应答。然而主机在接收到每个已经被从发送机终止的字节后必须产生一个应答。在应答时钟脉冲期间,应答的器件必须将SDA线拉低,因此在应答相应的时钟脉冲的高电平期间,SDA线必须保持稳定的低电平。在由从机终止的最后一个字节主接收机必须通过产生一个低电平应答向发送机发出一个数据结束信号,这样发送机必须将数据线拉高以允许主机产生停止条件
在这里插入图片描述
发送应答:

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;
	
    SCL  = 1;
    IIC_Delay(DELAY_TIME);
    ackbit = SDA;
    SCL = 0;
    IIC_Delay(DELAY_TIME);
    return ackbit;
}

3.1.4、空闲状态

I2C总线总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高

3.1.5、写模式的总线协议

在这里插入图片描述

3.1.6、写模式的总线协议

在这里插入图片描述

2.4、PCF8951操作练习

读取Rb2的电压值并显示在数码管上和LED灯上,范围0.00~5.00V,通过改变滑动变阻器Rb2的阻值可改变电压值。
main.c

#include "STC15F2K60S2.h"
#include "iic.h"
#include "intrins.h"

typedef unsigned char uchar;
typedef unsigned int uint;

uchar dsp_code[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};

uchar dsp_adc[8]={0xff,0xff,0xff,0xff,0xff};

uint value;
uchar count_adc;

void time0_init(void)
{
  AUXR |= 0x80;
  TMOD &= 0XF0;
  TL0=0XCD;
  TH0=0xD4;
  TR0=1;
  ET0=1;
  EA=1;
}

void time0_isr(void) interrupt 1
{
  static uchar dsp_com=0;
  
  P0=0;P2=0xC0;P2=0;
  P0=dsp_adc[dsp_com];P2=0xe0;P2=0;
  P0=1<<dsp_com;P2=0xC0;P2=0;
  
  if(++dsp_com==8) dsp_com=0;
  
  ++count_adc;
}


void main()
{
  char i=0;
  
  P0=0xff,P2=0x80;P2=0;
  P0=0x00,P2=0xa0,P2=0;
  
  
  for(i=0;i<5;i++)
  {
    value = read_adc(0x43)*1.96+0.5;
  }
  
  time0_init();
  
  while(1)
  {
    
    if(count_adc > 200) //200ms 刷新一次A/D值
    {
      count_adc=0;
      EA=0;
      // value = read_adc(0x44);   对应读取光敏电阻 RD1
      //*1.96作用:题目要求显示的电压值为 0~5.00.而直接从该地址读取的数值范围是 0~255.
      //因此我们先把数值范围处理成0~500.小数点则直接通过操作数码管显示出来。
      //+0.5表示四舍五入
      value = read_adc(0x43)*1.96+0.5; //对应读取滑变电阻 Rb
      P0=~value,P2=0x80;P2=0;
      EA=1;
    }
    dsp_adc[5]=dsp_code[value/100]&0x7f; //处理小数部分
    dsp_adc[6]=dsp_code[value/10%10];
    dsp_adc[7]=dsp_code[value%10];
    
  }
}

iic.c
比赛时iic的操作官方会给出,自己修改在发送和接受后面加上一个IIC_WaitAck(); 并且自己完成函数read_adc即可

#include "iic.h"

#define DELAY_TIME 5

#define SlaveAddrW 0xA0
#define SlaveAddrR 0xA1

void IIC_Delay(unsigned char i)
{
    do{_nop_();}
    while(i--);        
}


void IIC_Start(void)
{
    SDA = 1;
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SDA = 0;
    IIC_Delay(DELAY_TIME);
    SCL = 0;	
}


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;
	
    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;  
    
    //直接将等待应答函数写到此处
    // 后面写读取函数or 转换函数时就不用一直重复写这个函数
}

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);
    }
    
    IIC_WaitAck(); //直接将等待应答函数写到此处
    // 后面写读取函数or 转换函数时就不用一直重复写这个函数
    return da;    
    
}


//注意∶写设备和读设备地址只相差1
unsigned char read_adc(unsigned char add)
{
  unsigned char val;
  
  IIC_Start();
  IIC_SendByte(0x90); // MCU 向 PCF8591发送"写设备"命令
  IIC_SendByte(add);
  
  IIC_Start();
  IIC_SendByte(0x91); //MCU 向 PCF8591发送"读设备"命令
  val =IIC_RecByte(); //读取 A/D转换值 0-255
  
  IIC_Stop();
  return val;
  
}


iic.h

#ifndef _IIC_H
#define _IIC_H

#include <STC15F2K60S2.H>
#include "intrins.h"

sbit SDA = P2^1;
sbit SCL = P2^0;

void IIC_Start(void); 
void IIC_Stop(void);  
bit IIC_WaitAck(void);  
void IIC_SendAck(bit ackbit); 
void IIC_SendByte(unsigned char byt); 
unsigned char IIC_RecByte(void); 
unsigned char read_adc(unsigned char add); //这个自己加上的

#endif
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值