蓝桥杯学习三、PCF8951

目录

3.1PCF8591介绍

3.1.1、PCF8591简介

3.1.2、PCF8951地址

3.1.3、PCF控制字

3.1.4、PCF内部框图学习:

AD转换:

DA转换:

3.2进阶实验

题目解析:

3.3参考代码:

前言:

具体代码部分:


3.1PCF8591介绍

3.1.1、PCF8591简介

PCF8591是单片、单电源低功耗8位CMOS数据采集器件,具有4个模拟输入:用做AD转换通道本单片机只用到了通道1与通道3(光敏电阻与可调电阻)、一个输出(用作DA转换通道)和一个串行I2C总线接口(与单片机进行通信)。3个地址引脚A0、A1和A2用于编程硬件地址,允许将最多8个器件连接至I2C,总线而不需要额外硬件。器件的地址、控制和数据通过两线双向I2C总线传输。器件功能包括多路复用模拟输入、片上跟踪和保持功能、8位模数转换和8位数模拟转换。最大转换速率取决于I2C总线的最高速率。

上图的各个引脚说明如下:

  1. AIN0~AIN3:模拟信号输入端。
  2. A0~A3:引脚地址端。
  3. VDD、Vss:电源端。 (2.5~6V)
  4. SDA、SCL:IC 总线的数据线、时钟线。
  5. OSC:外部时钟输入端,内部时钟
  6. EXT:内部、外部时钟选择线,使用内部时钟时 EXT 接地。
  7. AGND:模拟信号地。
  8. AOUT:D/A 转换输出端。
  9. VREF:基准电源端。

PCF8591与光敏电阻可调电阻电路图如下:

由此图我们可以得到A0,A1,A2的引脚已经全部接地所以它的值全部为0,寻址时,只需要写是读还是写即可。

3.1.2、PCF8951地址

I2C 总线系统中的每一片PCF8591通过发送有效地址到该器件来激活(I2C总线片选)。该地址包括固定部分和可编程部分。可编程部分必须根据地址引脚A0、A1和A2来设置。在I2C总线协议中地址必须是起始条件后作为第一个字节发送。地址字节的最后一位是用于设置以后数据传输方向的读/写位。(具体地址段如下图)

具体控制指令格式如下:

  1. 高四位在出厂之前就已经确定,属于不可修改部分
  2. A0~A2是可编程部分,通过电路将对应的引脚拉高/拉低来确定,但蓝桥杯单片机将这三位全部接地,所以这三位全部为0。
  3. 第八位是读写控制位,为1时表示通信方向为,PCF写数据,单片机读数据。为0时则相反,所以对于地址的输入只有两种:单片机读PCF写(0x91),单片机写PCF读(0x90)。
3.1.3、PCF控制字

在通信寻址成功之后(片选+读/写),就是对PCF写控制指令,设置芯片的工作模式,其格式如下所示:

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

当系统为A/D转换时,模拟输出允许为0。模拟量输入选择位取值由输入方式决定:四路单端输入时取00,三路差分输入时取01,单端与差分输入时取10,二路差分输入时取11。最低两位时通道编号位,当对0通道的模拟信号进行A/D转换时取00,当对1通道的模拟信号进行A/D转换时取 01,当对2通道的模拟信号进行A/D转换时取10,当对3通道的模拟信号进行A/D转换时取 11。

3.1.4、PCF内部框图学习:

其内部框图如下:

根据PCF的两种操作来对PCF进行学习:

AD转换:

AD转换采用逐次逼近型的工作原理:当通道输入的模拟电压到达逐次逼近寄存器时,PCF内部拥有一个DAC模块,其内部是通过加权电阻网络实现模数转换,可以将逐次逼近寄存器的值转换为对应的模拟电压值,将其电压值再与待测电压相比较(在比较器中进行比较),比较结果控制逐次逼近寄存器中存储的值,直到DAC输出的电压与外部通道输入的电压近似相等,DAC输入的数据就是外部电压的编码数据了。为了最快找到未知编码的电压,通常使用二分法进行查找。且使用二分法查找未知电压的编码的好处在于:每次选择比较的值恰好为对应二进制数字的每一位的权数,判断过程相当于,从高位到底位依次判断为1还是为0的过程(每次对比是比找的数大还是小)。要找到未知编码的电压,8位ADC需要判断8次,12位ADC需要判断12次。转换结束后,ADC的输入数据就是未知电压的编码,通过I2C数据线进行数据传输。

具体转换流程如下:

【1】 主机发送开始通信信号

【2】 主机寻址,与PCF8591通信(方向:单片机-> PCF8591)

【3】 等待PCF8591应答,

【4】 应答成功,发送控制指令(AD转换,单端与差分配合输入,通道为channel)

【5】 等待PCF8591应答,

【6】 应答成功,主机结束通信

【8】 等待电压转换完成(如果I2C通信速率慢,且没有这一步的话容易读不出来电压值)

【7】 主机开始通信

【8】 主机寻址,与PCF8591通信(方向: PCF8591->单片机)

【9】 等待PCF8591应答,

【10】 PCF8591发送AD转换后的数字量

【11】 主机读取数据,并且不应答

【12】 主机结束通信

【13】 主机对读取的数据进行处理,

具体代码如下:

unsigned char PCF8591_AD_Read(unsigned char Channel){
	unsigned char dat;
	  IIC_Start();     //主机通信     
	  IIC_SendByte(PCF8591W);    //PCF8591的写设备地址 
	  IIC_WaitAck();   //等待应答 
	  IIC_SendByte(Channel);   //发送控制字(要读取的通道)
	  IIC_WaitAck();    //等待应答          
	  IIC_Stop(); //结束通信
	  Delayxms(10);//等待电压转换完成
	  IIC_Start(); //主机开始通信          
	  IIC_SendByte(PCF8591R);   //PCF8591的读设备地址
	  IIC_WaitAck();             //等待应答    
	  dat = IIC_RecByte(); //读出AD采样数据
	  IIC_SendAck(1);         //产生非应答信号                 
	  IIC_Stop();	//结束通信
	return  dat;
	
}
DA转换:

单片机将写入的数据发送到PCF8591设备的第三个字节存储在DAC数据请求器中,并使用片上D/A转换器转换为相应的模拟电压。具体过程与AD像类似不再过多阐述,其具体的工作流程如下:

【1】 主机开始通信信号

【2】 主机寻址,与PCF8591通信(方向:单片机-> PCF8591)

【3】 等待PCF8591应答,

【4】 应答成功,发送控制指令(DA转换)

【5】 等待PCF8591应答,

【6】 应答成功,单片机继续发送DA转换的数字量

【7】 等待PCF8591应答,

【8】 应答成功,PCF8591进行DA转换

【9】 主机结束通信

具体代码如下:

void PCF8591_AD_Write(unsigned char dat){//DA转换,单片机写入
	I2C_Start();//开始通信
	I2C_SendByte(PCF8591W);//写入写地址
	I2C_waitACK();//等待应答
	I2C_SendByte(PCF8591_Write_Control); // 写入允许DA转换
	I2C_waitACK();//等待应答
	I2C_SendByte(dat);//发送DA转换的值
	I2C_waitACK();//等待应带
	I2C_Stop();//结束通信
}

3.2进阶实验

  • 将IIC总线的底层驱动代码文件正确移植到工程中。
  •  光敏电阻rd1接到PCF8591的ANI1通道,可调电阻rd3接到PCF8591的ANI3通道。
  • 将J5的23脚短接,把S4、S5和S6设置为独立按键。
  •  系统上电后,循环采样PCF8591的AIN1通道,并将采 样结果换算成电压值,电压结果保留2位小数,并根据电压值 控制8个LED灯的开关。
  •  灯光的控制分为五个等级:

等级1:5V <= 光照电压(可调电阻电压) < 4V,L1点亮。

等级2:4V <= 光照电压 (可调电阻电压)< 3V,L1至L2点亮。

等级3:3V <= 光照电压 (可调电阻电压)< 2V,L1至L4点亮。

等级4:2V <= 光照电压 (可调电阻电压)< 1V,L1至L6点亮。

等级5:1V <= 光照电压 (可调电阻电压)< 0V,L1至L8点亮。

  •  灯光控制等级和光敏电压实时显示在数码管上,在数码管靠左端的四位,第一位显示通道口,剩下三位显示当前灯光的控制等级,在数码管靠右端的三位,显示光敏电阻(可调电阻的电压)的当前电压。
  •  按下S6按键,切换数码管显示的电压,初始化为光敏电阻的电压。
  • 按下S5按键,将当前灯光的控制等级和光敏电阻的采样数据保存到24C02存储器的 0x01 和0x02 地址单元中。
  • 按下S4按键时,读取存储在24C02中的历史数据并显示在数码管上,再次按下时,回到切换电阻电压的界面。
题目解析:

这个看起来要求很多其实细分的话就四个模块:LED灯控制函数,按键控制函数,I2C总线协议的综合运用,数码管的使用,其中最主要的是I2C总线协议的使用和按键控制,这两个在蓝桥杯比赛中也是重难点,按键的循环嵌套非常考验编程技巧,希望大家和我共同加油努力!!!!。

3.3参考代码:

前言:

由于我的板子学校给的好像有点拉,所以我的板子对于I2C协议适配度有点问题,为此d了有点久的代码,所以本代码只做参考,哈哈哈

具体代码部分:

main.c

#include "System.h"

void main(){
	Init_System(); //系统初始化
	Timer0_Init();//定时器扫描按键
	while(1){
		Key_Control();//按键按下改变通道
		LED_Control();//根据电压设置的照明控制
		Display_SMG();//数码管显示函数
	
	}
}

I2C_System.c(这里的两个模块我的IIC协议用的两个(单片机学校给的不是新版有点不适配底层驱动,我d了两天bug我确定是IIC协议传输速率的问题(因为其他网上的代码也过不了),所以我这样用了,新板子应该不会出现我这种情况(不给我新板子,我拿什么拿奖,八嘎学校!!!!)))

#include "I2C_System.h"


void AT24C02_Write(unsigned char address,unsigned char Byte){
	
	I2C_Start();//开始通信
	I2C_SendByte(SlaveAddrW);//发送写地址
	I2C_waitACK();//等待应答
	I2C_SendByte(address);//发送数据要写入的地址
	I2C_waitACK();//等待应答
	I2C_SendByte(Byte);//在指定的地址中写入数据
	I2C_waitACK();//等待应答
	I2C_Stop();//结束通信
	
}

unsigned char  AT24C02_Read(unsigned char address){
	unsigned char  Byte= 0;
	
	I2C_Start();//开始通信
	I2C_SendByte(SlaveAddrW);//写入写标志
	I2C_waitACK();//等待应答
	I2C_SendByte(address);//写入要写的地址
	I2C_waitACK();//等待应答
	I2C_Start();//重启开始通信
	I2C_SendByte(SlaveAddrR);//写入读标志
	I2C_waitACK();//等待应答
	Byte = I2C_ReceiveByte();//读取内容
	I2C_SendACK(1);//发送停止信号
	I2C_Stop();//结束通信
	
	return Byte;
}
unsigned int PCF8591_AD_Read(unsigned char Channel){
	unsigned int real_dat;
	unsigned char dat;
	float  value;
	  IIC_Start();          
	  IIC_SendByte(PCF8591W);    //PCF8591的写设备地址 
	  IIC_WaitAck();    
	  IIC_SendByte(Channel);   
	  IIC_WaitAck();              
	  IIC_Stop(); 
	//  Delayxms(10);
	  Display_SMG();
	  IIC_Start();                  
	  IIC_SendByte(PCF8591R);   //PCF8591的读设备地址
	  IIC_WaitAck();                 
	  dat = IIC_RecByte(); //读出AD采样数据
	  IIC_SendAck(1);         //产生非应答信号                 
	  IIC_Stop();	
	//数字规格化处理
	  value = dat *(5.0 / 255);
	  real_dat = value/100;
	  real_dat = (real_dat * 100) + ( value * 100 - real_dat * 100);
	return  real_dat;
	
}
/*
void PCF8591_AD_Write(unsigned char dat){
	I2C_Start();//开始通信
	I2C_SendByte(PCF8591W);//写入写地址
	I2C_waitACK();//等待应答
	I2C_SendByte(PCF8591_Write_Control); // 写入允许DA转换
	I2C_waitACK();//等待应答
	I2C_SendByte(dat);//发送DA转换的值
	I2C_waitACK();//等待应带
	I2C_Stop();//结束通信
}*/

System.c

#include "System.h"
uchar flag = 0;//改变显示模式
uchar lastkey,nowkey;//获取键值
uint value=0;//获取AD通道的值

uchar display_24C02_level = 0;//读取当前的照明等级
uchar display_24C02_AIN = 0;//读取当前的电阻通道
uint display_24C02_value = 0;//读取当前的电阻电压
uchar lastflag = 0;//区分读取历史电压与当前电压
uchar level;//照明等级
							/*0    1    2    3     4    5    6    7    8    9 */
uchar code NixieTube_nodot[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,
							/* A   B     C    D    E    F    H    L    N    P */
							0x88,0x83,0xC6,0xA1,0x86,0x8E,0x89,0xC7,0xC8,0x8C,
							/* U    -   ' ' */
							0xC1,0xBF,0xFF};//无小数点

uchar code NixieTube_havedot[]={0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};//有小数点

void Delay(uchar xms)	//@12MHz延时函数
{
	unsigned char data i, j;
	while(xms--){
	i = 12;
	j = 190;
	do
	{
		while (--j);
	} while (--i);
	}
	
}

void HC138_Select(uchar choice){//锁存器选择函数
	
	switch(choice){
		case 0: P2 = (P2 & 0x1f) | 0x00; break;
		case 4: P2 = (P2 & 0x1f) | 0x80; break;
		case 5: P2 = (P2 & 0x1f) | 0xa0; break;
		case 6: P2 = (P2 & 0x1f) | 0xc0; break;
		case 7: P2 = (P2 & 0x1f) | 0xe0; break;
	}
	
}
void Init_System(void){//系统初始化
	HC138_Select(4);
	P2 = 0xFF;
	HC138_Select(5);
	P2 = 0x00;
	HC138_Select(6);
	P2 = 0xFF;
	HC138_Select(7);
	P2 = 0xFF;
}
void SMG_TranslateBit(uchar pos,uchar value){//数码管显示一位函数
	HC138_Select(7);
	P0 = 0xFF;
	HC138_Select(6);
	P0 = 0x01<<pos;
	HC138_Select(7);
	P0 = value;
	Delay(2);
}

void LED_Control(void){//根据电压值获取照明的亮度
	if(lastflag == 0){
		if(flag == 0){
			if(value>=0 && value < 100){
				HC138_Select(4);
				P0 = 0x00;//L1到L8全亮
				level = 5;
			}
			if(value>=100 && value < 200){
				HC138_Select(4);// L1至L6亮
				P0 = 0xC0;
				level = 4;
			}
			if(value>=200 && value < 300){
				HC138_Select(4);
				P0 = 0xF0; // L1到L4全亮
				level = 3;
			}
			if(value>=300 && value < 400){
				HC138_Select(4);
				P0 = 0xFC; // L1到L2全亮
				level = 2;
			}
			if(value>=400 && value < 500){
				HC138_Select(4);
				P0 = 0xFE; // L1亮
				level = 1;
			}
		}
		if(flag == 1){
			if(value>=0 && value < 100){
				HC138_Select(4);
				P0 = 0x00;//L1到L8全亮
				level = 5;
			}
			if(value>=100 && value < 200){
				HC138_Select(4);// L1至L6亮
				P0 = 0xC0;
				level = 4;
			}
			if(value>=200 && value < 300){
				HC138_Select(4);
				P0 = 0xF0; // L1到L4全亮
				level = 3;
			}
			if(value>=300 && value < 400){
				HC138_Select(4);
				P0 = 0xFC; // L1到L2全亮
				level = 2;
			}
			if(value>=400 && value < 500){
				HC138_Select(4);
				P0 = 0xFE; // L1亮
				level = 1;
			}
		}
	}
	else if(lastflag == 3){
		HC138_Select(4);
		P0 = 0xFF;
	}

}
void Mode_Control(){//电压选择显示函数
	if(flag == 0){	
	value = PCF8591_AD_Read(0x41);
	}
	else if(flag == 1){
	value = PCF8591_AD_Read(0x43);
	}
}
void Save_to_24C02()
{
		if(flag == 0)
		{AT24C02_Write(0x00,0x01);}//保存通道
		else if(flag == 1)
		{AT24C02_Write(0x00,0x03);}
		Display_SMG();
		AT24C02_Write(0x01,	level);//保存照明等级
		Display_SMG();
		AT24C02_Write(0x02,value);//保存获取的的电压值
		Display_SMG();
}
void Read_from_24C02()//读取保存在AT24C02中的信息
{
	display_24C02_AIN = AT24C02_Read(0x00);//读保存的AD通道
	display_24C02_level = AT24C02_Read(0x01);//读保存的照明等级
	display_24C02_value = (int)AT24C02_Read(0x02); //读保存的电压

}
void Key_Control(void){//按键控制
	Mode_Control();
	lastkey = nowkey;
	nowkey = Key_Read();

	if(lastkey == 6){
		if(lastkey == 6){
			if(flag == 0){flag = 1;  }
			else if(flag ==  1) {flag = 0;}
		}
	}
	if(lastkey == 5){//保存电压值
		if(lastkey == 5)
		Save_to_24C02();
	}
	if(lastkey == 4){ //读出电压值
		if(lastkey == 4){
				if(lastflag == 0){
				Read_from_24C02();
				 lastflag = 3;	
			}
			else if(lastflag == 3){
				 lastflag = 0;	
			}
		}
	}
}
void Display_SMG(){//数码管显示函数
		if(lastflag == 0){
			if(flag == 0){
			SMG_TranslateBit(0,NixieTube_nodot[1]);
			}
			else{
				SMG_TranslateBit(0,NixieTube_nodot[3]);
			}
			SMG_TranslateBit(1,NixieTube_nodot[21]);
			SMG_TranslateBit(2,NixieTube_nodot[level]);
			SMG_TranslateBit(3,NixieTube_nodot[21]);
			SMG_TranslateBit(5,NixieTube_havedot[value/100]);
			SMG_TranslateBit(6,NixieTube_nodot[value/10%10]);
			SMG_TranslateBit(7,NixieTube_nodot[value%10]);
		}
		else if(lastflag == 3){
			SMG_TranslateBit(0,NixieTube_nodot[display_24C02_AIN]);
			SMG_TranslateBit(1,NixieTube_nodot[21]);
			SMG_TranslateBit(2,NixieTube_nodot[display_24C02_level]);
			SMG_TranslateBit(3,NixieTube_nodot[21]);
			SMG_TranslateBit(5,NixieTube_havedot[display_24C02_value/100]);
			SMG_TranslateBit(6,NixieTube_nodot[display_24C02_value/10%10]);
			SMG_TranslateBit(7,NixieTube_nodot[display_24C02_value%10]);
			
		}
}

Key.c

#include "key.h"
#include "I2C_System.h"


void Timer0_Init(){//定时器初始化
	TMOD = 0x00;
	TL0 = 0x18;
	TH0 = 0x66;
	TR0 = 1;
	EA = 1;
	ET0 = 1;
	PT0 = 0;
	
}
unsigned char Key_Scan(void){//读取按键键值
	uchar KeyNumber = 0;
	if(Key6==0){ if(Key6==0)KeyNumber = 6;}
	if(Key5==0){ if(Key5==0)KeyNumber = 5;}
	if(Key4==0){ if(Key4==0)KeyNumber = 4;}
	return KeyNumber;
}
unsigned char Key_Read(void){//获取按键键值
	static uchar Laststate,nowstate;
	uchar Keynum;
	Laststate = nowstate;
	nowstate = Key_Scan();
	if(Laststate == 6 && nowstate == 0) Keynum = 6;
	if(Laststate == 5 && nowstate == 0) Keynum = 5;
	if(Laststate == 4 && nowstate == 0) Keynum = 4;
	
	return Keynum;
}
 
void Timer0_handel(void) interrupt 1{
	static uint T0Count = 0;
	T0Count++;
	if(T0Count>=20){
		T0Count = 0;
		Key_Read();//用定时器扫描按键
	}
}

  • 34
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
蓝桥杯单片机开发板CT107D中的板载A/D转换芯片PCF8591可进行ADC和DAC的转换。该芯片有4个输入通道,其中AIN3引脚接滑动变阻器Rb2,通过旋转滑动变阻器Rb2可以改变AIN3引脚的输入电压。AIN1引脚接光敏电阻RD1,通过改变光敏电阻RD1的进光量可以改变AIN1引脚的输入电压。PCF8591芯片通过IIC通信协议与单片机进行数据传输,其地址为1001000。如果单片机进行写操作,则发送的地址为0x90;如果单片机进行读操作,则发送的地址为0x91。滑动变阻器和光敏电阻两部分的代码基本一样,唯一的不同就是单片机在给芯片发送指令时,滑动变阻器为0x43,光敏电阻为0x41。\[3\] 所以,蓝桥杯PCF8591DA转换是通过PCF8591芯片进行的,通过改变滑动变阻器和光敏电阻的输入,可以改变相应引脚的输入电压,从而实现模拟信号的转换。 #### 引用[.reference_title] - *1* *2* [PCF8591芯片的AD/DA转换(适用于蓝桥杯单片机)](https://blog.csdn.net/wcl501375/article/details/129626609)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [蓝桥杯单片机-ADC-A/D转换-测量PCF8591中AIN3通道(滑动变阻器Rb2)的电压](https://blog.csdn.net/diksan/article/details/123034823)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值