蓝桥杯单片机竞赛学习计划(八)

本文介绍了PFC8591这款单片机AD/DA转换器,其用于模拟信号处理,支持I2C通信。详细讲解了其引脚功能、工作原理和在实际项目中的应用,如通过数码管显示光敏电阻和电位器的AD转换结果。
摘要由CSDN通过智能技术生成

蓝桥杯单片机学习计划之PFC8591


前言

本来打算这一节介绍DS18B20温度传感器的内容,但是想了想既然上一节已经介绍过使用I2C协议通信的AT24C02了,所以这一节干脆把PFC8591一起介绍了,正好这两都是使用I2C协议通信的。


提示:以下是本篇文章正文内容,下面案例可供参考

一、PFC8591介绍

PFC8591是八位AD/DA转换器,即模数/数模转换器,可以将输入的模拟信号转换成数字信号输出,也可将输入的八位数据转换成模拟信号输出,今天我们主要使用它的A/D功能,即读取外部输入的模拟信号。下面简单介绍一下PFC8591:
PFC8591是一款单电源供电、低功耗8位CMOS数据采集器件,它有思路模拟输入和一个I2C串行总线接口,另外有三个地址引脚A0、A1、A2可用于编程设备地址(和AT24C02一样的)。下面这张图中可以看到它的一些特性。
在这里插入图片描述
它的内部框图就不看了,我们主要来看一下它的引脚介绍:
首先AIN0到AIN3是它的四个模拟输入引脚,即可以接四路模拟输入;A0-A2应该是比较熟悉的了,上一节在AT24C02中刚介绍过,就是地址输入引脚,这里就不再赘述了;Vss是接地引脚;SDA和SCL也就不介绍了,就是I2C的数据引脚和时钟引脚;OSC和EXT这两个引脚是关于外部晶振的引脚,OSC是连接外部晶振的引脚,如果连接了外部晶振,则EXT需要连接到VDD,如果不使用,则EXT连接到地,由于PFC8591内部有晶振,所以一般不使用外部晶振,可以看到竞赛板上是直接接地的;AGND是模拟地引脚;Vref是参考电压输入引脚,即基准电压,可以看到竞赛板上直接连接到了VCC;AOUT是模拟输入引脚,即输入的八位数据值可以通过这个引脚输出对应的模拟值;VDD是供电电源引脚。
在这里插入图片描述
上面已经将芯片的所有引脚都介绍过了,这边再来看下竞赛板的原理图,可以看到AIN1连接到了光敏电阻RD1,AIN3连接到了电位器Rb2,至于AIN0和AIN3,一个没有接输入,另一个连接的是运放的差分输出,我们就暂时不去管了。A2-A0连接到了地,SCL和SDA同样是连接到了P20和P21,因为使用的是I2C协议,总线上可以挂载多个设备,根据设备地址来进行数据通信,所以每个设备的这两条线都是分别连接到一起的。
在这里插入图片描述
接着来看下PFC8591的设备地址,高四位固定的1001,低四位和AT24C02一样的,前三位通过A2-A0选择,最后一位读写选择位,所以它的写设备地址应该是0x90,读地址是0x91。
在这里插入图片描述
PFC8591有一个控制字节,在读取转换结果前,需要先发送这个控制字节,我们来看下这个控制字节的每一位分别是什么意思:
最高位是固定的0;第6位是模拟输出使能标志位,要开启模拟输出的时候,将这个位设置为1,不开启则设置为0,我们一般都是读取外部模拟输入的转换结果,所以这一位正常情况下置0就可以;第5位和第4位是模拟输入方式选择,设置成11是四个通道变成两个差分输入通道,设置成10是两个单独通道和一个差分输入通道,设置成01是三个差分输入通道,设置成00是四个单独的模拟输入通道,正常情况下我们设置一般都会设置成00,使用四个模拟输入通道;第3位是固定的0;第2位是自动增量标志位,即如果读取完通道0的转换结果,还想继续读取通道1的转换结果,就需要将这一位置1,相当于连续扫描模式,转换完通道0会自动转换通道1,转换完通道1则转换通道2…如果设置成0,则只会转换当前选择的通道;最后两位就是选择想要转换的通道:
00——通道0
01——通道1
10——通道2
11——通道3
在这里插入图片描述
通讯时序这边就不再介绍了,上一节已经介绍过了,主要来看下一个比较重要的参数———转换时间,PFC8591是一款逐次逼近型的AD转换器,转换时间一般在几十微秒左右,从下图中可以看到,最大转换时间是90us。
在这里插入图片描述
下面来看程序部分。

三、代码

下面这个程序实现的是读取光敏电阻和电位器的AD转换结果并通过数码管显示出来,前三个数码管显示的是电位器的转换结果,最后三个数码管显示的是光敏电阻的转换结果。另外上次AT24C02忘记把IIC部分的代码贴出来了,这里补上,其实这两个器件通信方式几乎是一样的,只是有一点细微的差别。

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

bit timer_flag_ms;

unsigned char SEG_Code[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};//0-9段码
unsigned char SEG_Buf[8]={0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0};//显示数据缓冲区

unsigned char rd1_voltage;//暂存光敏电阻AD转换结果
unsigned char rb2_voltage;//暂存电位器AD转换结果

void Seg_Display()//数码管扫描函数
{
	static unsigned char seg_index=0;//显示数据索引
	static unsigned char seg_temp=0x01;//位索引
	
	
	P2 = (P2&0x1f) | 0xC0;  //消隐
	P0= 0x00;
	P2=0x00;
	
	P2 = (P2&0x1f) | 0xE0;//选通段锁存器
	P0=SEG_Buf[seg_index++]; //送入段码
	P2=0x00;
	
	P2 = (P2&0x1f) | 0xC0;//选通位锁存器
	P0= seg_temp;//送入位码
	P2=0x00;
	seg_temp<<=1;
	if(seg_index>7)
	{
		seg_index=0;
		seg_temp=0x01;
	}
}
void main()
{
	unsigned char cnt;
	AUXR &= 0x7F;		//定时器时钟12T模式
	TMOD &= 0xF0;		//设置定时器模式
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0=1;      //开启定时器0中断
	EA=1;       //开启总中断

	while(1)
	{
		if(timer_flag_ms)//1ms
		{
			timer_flag_ms=0;
			if(++cnt>=100)//100ms获取一次数据
			{
				cnt=0;
				rd1_voltage=Pfc8591_Read(1);//获取光敏AD转换结果
				rb2_voltage=Pfc8591_Read(3);//获取电位器AD转换结果
				SEG_Buf[0]=SEG_Code[rd1_voltage/100];
				SEG_Buf[1]=SEG_Code[rd1_voltage%100/10];
				SEG_Buf[2]=SEG_Code[rd1_voltage%10];
				SEG_Buf[3]=SEG_Code[0];
				SEG_Buf[4]=SEG_Code[0];
				SEG_Buf[5]=SEG_Code[rb2_voltage/100];
				SEG_Buf[6]=SEG_Code[rb2_voltage%100/10];
				SEG_Buf[7]=SEG_Code[rb2_voltage%10];
			}
		}
	}
}
void Timer0_Rountine(void)   interrupt 1    //定时器0中断,1ms
{
	timer_flag_ms=1;
	Seg_Display();//使用定时器进行数码管扫描
}

**这是IIC文件**
#include "iic.h"
#define DELAY_TIME 5

//I2C总线内部延时函数
void IIC_Delay(unsigned char i)
{
    do{_nop_();}
    while(i--);        
}

//I2C总线启动信号
void IIC_Start(void)
{
    SDA = 1;
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SDA = 0;
    IIC_Delay(DELAY_TIME);
    SCL = 0;	
}

//I2C总线停止信号
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;
}

//I2C总线发送一个字节数据
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;  
}

//I2C总线接收一个字节数据
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;    
}
unsigned char Pfc8591_Read(unsigned char ad_channel)
{
	unsigned char dat;
	IIC_Start();
	IIC_SendByte(0x90);
	IIC_WaitAck();
	IIC_SendByte(0x00|ad_channel);
	IIC_WaitAck();
	IIC_Start();
	IIC_SendByte(0x91);
	IIC_WaitAck();
	dat=IIC_RecByte();
	IIC_SendAck(1);
	IIC_Stop();
	return dat;			
}

以上为本次实验的程序,显示光敏和电位器的转换结果

以下是上一次AT24C02的IIC文件

#include "iic.h"

#define DELAY_TIME 5

//I2C总线内部延时函数
void IIC_Delay(unsigned char i)
{
    do{_nop_();}
    while(i--);        
}

//I2C总线启动信号
void IIC_Start(void)
{
    SDA = 1;
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SDA = 0;
    IIC_Delay(DELAY_TIME);
    SCL = 0;	
}

//I2C总线停止信号
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;
}

//I2C总线发送一个字节数据
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;  
}

//I2C总线接收一个字节数据
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;    
}
void AT24C02_Byte_Write(unsigned char addr,unsigned char dat)//AT24C02单字节写入
{
	IIC_Start();
	IIC_SendByte(0xA0);
	IIC_WaitAck();
	IIC_SendByte(addr);
	IIC_WaitAck();
	IIC_SendByte(dat);
	IIC_WaitAck();
	IIC_Stop();
}
unsigned char AT24C02_Byte_Read(unsigned char addr)//AT24C02单字节读取
{
	unsigned char dat;
	unsigned char ack;
	do  //等待AT24C02写入完毕
	{
		IIC_Start();
		IIC_SendByte(0xA0);
		ack=IIC_WaitAck();
	}while(ack==1);
	IIC_SendByte(addr);
	IIC_WaitAck();
	IIC_Start();
	IIC_SendByte(0xA1);
	IIC_WaitAck();
	dat=IIC_RecByte();
	IIC_SendAck(1);
	IIC_Stop();
	return dat;			
}

总结

那么这一节到这里就结束了,下一节再介绍DS18B20温度传感器。

  • 35
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值