蓝桥杯单片机 | 特训案例【进阶07】基于PCF8591的电压信号探测器

【1】题目要求

新建工程,以I/O模式编写代码,在CT107D单片机综合训练平台上,实现以下功能:

1、将IIC总线的底层驱动代码文件正确移植到工程中。

2、将J5配置BTN模式,把S4S5S6设置为独立按键。

3、系统上电后,电压参数3.00V报警计数0。单片机循环采样PCF8591芯片AIN3通道的输入电压,并将该电压与电压参数比较。采样电压小于电压参数时,启动报警计时,报警计数值每秒钟加1累计。报警持续超过2秒钟L1点亮;超过4秒钟L1L2点亮,超过6秒钟L1L2L3点亮,并且L8开始循环秒闪,即点亮1秒,熄灭1秒。直到采样电压大于等于电压参数,报警计时解除,报警计数清0,全部指示灯熄灭,秒闪停止。

3、通过数码管实现电压数据参数设置报警计数三个界面。系统上电后,默认工作在电压数据界面,如下图,电压数据为PCF8591芯片AIN3通道的实时电压。


        在电压数据界面中按下S4按键,进入参数设置界面,如下图,仅在该界面中,S5S6按键有效。按下S5按键,电压参数减小0.5V,减小到0.00V后,再次按下S5返回5.00V;按下S6按键,电压参数增加0.5V,增加到5.00V后,再次按下S6返回0.00V

        在参数设置界面中按下S4按键,进入报警计数界面,如下图,报警计数使用2位数码管,未用的位数熄灭,最大值为99,当到达99时,停止不再累计。

         在报警计数界面中按下S4按键,重新返回电压数据界面如此循环切换界面。

【小提示】:

IIC接口的基本原理,可见本博客:《【蓝桥杯单片机进阶强化-01】IIC总线接口技术基础》

PCF8591的基本原理,可见本博客:《【蓝桥杯单片机进阶强化-02】PCF8591的基本原理与A/D转换应用》

【2】核心源码分析

/*==================蓝桥杯单片机特训==================
【进阶06】:基于PCF8591的DAC模拟电压输出
**平  台:CT107D单片机综合实训平台
**模  式:IO模式
**底层驱动文件:2022年竞赛资源数据包提供的文件
**设  计:欧浩源(小蜜蜂老师,ohy3686@qq.com)
**时  间:2022-04-05
**更多详见:www.xmf393.com
====================================================*/

#include "reg52.h"
#include "iic.h"

sbit S4 = P3^3;
sbit S5 = P3^2;
sbit S6 = P3^1;

//定义动态显示中单个数码管点亮时长
#define TSMG	500
//-------共阳数码管的段码编码表(无小数点)--------
//0 1 2 3 4 5 6 7 8 9 A B C D E F - .
unsigned char code SMG_NoDot[18]={0xc0,0xf9,
	  0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,
    0x88,0x80,0xc6,0xc0,0x86,0x8e,0xbf,0x7f};
//-------共阳数码管的段码编码表(带小数点)--------
//0. 1. 2. 3. 4. 5. 6. 7. 8. 9.
unsigned char code SMG_Dot[10]={0x40,0x79,
		0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};
//-----------------------------------------------

unsigned int adc_value = 0;			//AIN3的采样数据
float adc_volt = 0;					//AIN3的换算电压
unsigned int smg_volt = 0;			//AIN3的显示电压
unsigned int param = 300;		    //电压参数,3.00V
unsigned char stat_smg = 1;			//当前显示界面标志
unsigned char num = 0;				//报警计数变量
unsigned int count_t = 0;
unsigned char stat_led = 0xff;      //LED灯控制状态
unsigned char F_shan = 0;           //启动秒闪灯标志
unsigned char F_time = 0;           //启动报警计时标志

/*====锁存器设置和数码管显示的代码参考前面的案例====*/
//=================系统信息数码管显示=================
void DisplaySMG_Info()
{
	switch(stat_smg)
	{
		//电压数据界面
		case 1:
		DisplaySMG_Bit(7,SMG_NoDot[smg_volt	% 10]);
		DisplaySMG_Bit(6,SMG_NoDot[(smg_volt / 10) % 10]);		
		DisplaySMG_Bit(5,SMG_Dot[smg_volt / 100]);				
		//提示符:U---->0xC1
		DisplaySMG_Bit(0,0xc1);	
		break;
		//参数设置界面
		case 2:
			DisplaySMG_Bit(7,SMG_NoDot[param	% 10]);
			DisplaySMG_Bit(6,SMG_NoDot[(param / 10) % 10]);		
			DisplaySMG_Bit(5,SMG_Dot[param / 100]);				
			//提示符:P---->0x8C
			DisplaySMG_Bit(0,0x8c);	
		break;
		//报警计数界面
		case 3:
			DisplaySMG_Bit(7,SMG_NoDot[num	% 10]);
			if(num / 10 != 0)
			{
				DisplaySMG_Bit(6,SMG_NoDot[num / 10]);		
			}				
			//提示符:S---->0x92
			DisplaySMG_Bit(0,0x92);	
		break;
		
	}
}
//===================定时器T0初始化===================
void Init_Timer0()
{
  //定时器计数初值设置:50ms
  TH0 = (0 - 50000) / 256;
  TL0 = (0 - 50000) % 256;
	TMOD = 0x01;	//T0的定时模式1:16位不可重装模式
  ET0 = 1;          //使能定时器0中断
  EA = 1;           //打开总中断
  TR0 = 1;          //启动定时器0
}
//===============定时器T0的中断服务函数================
void Service_Timer0() interrupt 1
{
  TH0 = (0 - 50000) / 256;
  TL0 = (0 - 50000) % 256;	

	if(F_time == 1)						//启动报警计时
	{
		count_t++;						//50ms单位定时累计
		
		if(count_t % 20 == 0)			//1秒时长
		{
			if(num != 99)				//没有到达最大值99前
			{
				num++;					//报警计数累计
			}
			if(F_shan == 1)				//启动秒闪功能
			{
				if((stat_led & 0x80) == 0x80)
				{
					stat_led &= ~0x80;	//点亮L8灯
				}
				else
				{
					stat_led |= 0x80;	//熄灭L8灯
				}
			}
		}
		
		if(count_t > 120)				//超过6秒
		{
			stat_led &= ~0x07;			//点亮L1 L2 L3
			F_shan = 1;
		}
		else if(count_t > 80)   	    //超过4秒
		{
			stat_led &= ~0x03;			//点亮L1 L2
		}
		else if(count_t > 40)			//超过2秒
		{
			stat_led &= ~0x01;			//点亮L1
		}
		else
		{
			stat_led = 0xff;
		}
	}
}
//===============PCF8591电压采样处理函数===============
unsigned char Read_PCF8591_AIN3()
{
	unsigned char tmp;
	IIC_Start();					
	IIC_SendByte(0x90);			//PCF8591的写设备地址 
	IIC_WaitAck();
	//选择AD转换通道
	IIC_SendByte(0x03); 		//AIN3通道,可调电阻电压
	IIC_WaitAck();  						
	IIC_Stop(); 
	DisplaySMG_Info();			//等待电压转换完成
	IIC_Start();									
	IIC_SendByte(0x91); 		//PCF8591的读设备地址
	IIC_WaitAck(); 	
	//读出AD采样数据	
	tmp = IIC_RecByte();
	IIC_SendAck(1);			    //产生非应答信号 								
	IIC_Stop();
	return tmp;
}

//=================电压数据应用分析处理=================
void App_Volt()
{
	unsigned char i;
	//采样通道3的电压,并做简单的均值滤波处理
	adc_value = 0;			
	for(i = 0; i < 3; i++)
	{
		adc_value += Read_PCF8591_AIN3();
	}
	adc_value = adc_value / 3;
	//将ADC采样到的数据换算成对应的电压值
	adc_volt = adc_value * (5.0 / 255);
	smg_volt = adc_volt * 100;

	//当前电压与电压参数的比较处理
	if(smg_volt < param)					//当前电压 < 电压参数
	{
		if(F_time == 0)					
		{
			F_time = 1;						//标志计算持续时间
		}
	}
	else									//当前电压 >= 电压参数
	{
		F_time = 0;							//恢复待计时状态
		F_shan = 0;
		count_t = 0;						//计时变量清0
		num = 0;
		stat_led |= 0xff;				    //指示灯L1熄灭
	}
	DisplaySMG_Info();			            //动态刷新数码管
	Set_HC573(4, stat_led);				    //更新LED灯控制状态
}
//===================按键扫描处理函数==================
void Scan_Keys()
{
	if(S4 == 0)									//切换界面
	{
		DelaySMG(500);	
		if(S4 == 0)
		{
			if(stat_smg == 1)
			{
				stat_smg = 2;					//切换至:参数设置
			}
			else if(stat_smg == 2)
			{
				stat_smg = 3;					//切换至:报警计数
			}
			else if(stat_smg == 3)
			{
				stat_smg = 1;					//切换至:电压数据
			}
			while(S4 == 0)						//松手检测
			{	
				DisplaySMG_Info();			    //保持数码管动态显示
			}
		}
	}
	
	if(S5 == 0)									//电压参数减小
	{
		DelaySMG(500);	
		if(S5 == 0)
		{
			if(stat_smg == 2)					//仅在参数设置中有效
			{
				if(param != 0)					//未减小到最小值
				{
					param = param - 50;		    //每按1下,减小0.5V
				}
				else
				{
					param = 500;				//边界处理
				}
			}
			while(S5 == 0)
			{	
				DisplaySMG_Info();
			}
		}
	}
	
	if(S6 == 0)									//电压参数增加
	{
		DelaySMG(500);	
		if(S6 == 0)
		{
			if(stat_smg == 2)					//仅在参数设置中有效
			{
				if(param != 500)				//未增加到最大值
				{
					param = param + 50;		    //每按1下,增加0.5V
				}
				else
				{
					param = 0;					//边界处理
				}
			}
			while(S6 == 0)
			{	
				DisplaySMG_Info();
			}
		}
	}
}

【注】:更多关于蓝桥杯单片机备赛内容,详见《蓝桥杯单片机设计与开发》小蜜蜂特训手册,本站可下载,相关更多蓝桥杯的案例完整源码及学习备赛笔记,欢迎进入 小蜜蜂笔记 公众号。

  • 27
    点赞
  • 82
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 17
    评论
基于PCF8591芯片AD DA实验例程C51单片机KEIL源码工程文件5个合集: PCF8591 1602液晶显示 PCF8591 1路AD数码管显示 PCF8591 4路AD数码管显示 PCF8591 DA输出模拟 PCF8591 输出锯齿波 main() { unsigned char num=0,i; unsigned char temp[7];//定义显示区域临时存储数组 float Voltage; //定义浮点变量 LCD_Init(); //初始化液晶 DelayMs(20); //延时有助于稳定 LCD_Clear(); //清屏 while (1) //主循环 { for(i=0;i<5;i++)//连续读5次,取最后一次,以便读取稳定值 num=ReadADC(0); //读取第1路电压值,范围是0-255 Voltage=(float)num*5/256; //根据参考电源VREF算出时间电压,float是强制转换符号,用于将结果转换成浮点型 sprintf(temp,"V0 %3.2f ",Voltage);//格式输出电压值,%3.2f 表示浮点输出,共3位数,小数点后2位 LCD_Write_String(0,0,temp); for(i=0;i<5;i++) num=ReadADC(1); Voltage=(float)num*5/256; sprintf(temp,"V1 %3.2f ",Voltage); LCD_Write_String(8,0,temp); for(i=0;i<5;i++) num=ReadADC(2); Voltage=(float)num*5/256; sprintf(temp,"V2 %3.2f ",Voltage); LCD_Write_String(0,1,temp); for(i=0;i<5;i++) num=ReadADC(3); Voltage=(float)num*5/256; sprintf(temp,"V3 %3.2f ",Voltage); LCD_Write_String(8,1,temp); //主循环中添加其他需要一直工作的程序 DelayMs(200); } } /*------------------------------------------------ 读AD转值程序 输入参数 Chl 表示需要转换的通道,范围从0-3 返回值范围0-255 ------------------------------------------------*/ unsigned char ReadADC(unsigned char Chl) { unsigned char Val; Start_I2c(); //启动总线 SendByte(AddWr); //发送器件地址 if(ack==0)return(0); SendByte(0x40|Chl); //发送器件子地址 if(ack==0)return(0); Start_I2c(); SendByte(AddWr+1); if(ack==0)return(0); Val=RcvByte(); NoAck_I2c(); //发送非应位 Stop_I2c(); //结束总线 return(Val); } /*------------------------------------------------ 写入DA转换数值 输入参数:dat 表示需要转换的DA数值,范围是0-255 ------------------------------------------------*/ /*bit WriteDAC(unsigned char dat) { Start_I2c(); //启动总线 SendByte(AddWr); //发送器件地址 if(ack==0)return(0); SendByte(0x40); //发送器件子地址 if(ack==0)return

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小蜜蜂老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值