当前位置: 首页 > news >正文

第16届蓝桥杯单片机4T模拟赛三

本次模拟赛涉及的模块:基础三件套(Led&Relay,按键、数码管)+ 进阶单件套(pcf8591的AD模块)

附件:
在这里插入图片描述

各模块底层代码在文章的结尾

一、数码管部分

1.页面1

页面1要显示的格式是:
最左边的数码管显示字母C,最后两位显示湿度的十位和个位(不足两位十位熄灭)

01234567
C30

在这里插入图片描述

湿度是由pcf8591的AD模块读取通道3的电压值转换来的,他们的关系是(从图转换成函数)
h u m i d i t y = { 10 , U ≤ 1 0.266 ∗ U − 16.666 ,    1 ≤ U ≤ 4 90 , U ≥ 4 humidity = \begin{cases} 10,U\le1\\ 0.266*U-16.666,\,\,1\le U \le 4\\ 90,U\ge 4\\ \end{cases} humidity= 10,U10.266U16.666,1U490,U4
所以在AD的处理函数就可以先将通道3的电压读出来后直接进行湿度转换。下面给出的是用float型变量接收未放大的电压值。

  • pcf8591.c底层代码
#include <STC15F2K60S2.H>
#include <intrins.h>

#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 ADRead()
{
   unsigned char temp = 0;
   I2CStart();
   I2CSendByte(0x90);
   I2CWaitAck();
   I2CSendByte(0x03);
   I2CWaitAck();
   
   I2CStart();
   I2CSendByte(0x91);
   I2CWaitAck();
   temp = I2CReceiveByte();
   I2CSendAck(1);
   I2CStop();
   
   return temp;
}
  • main.c中调用
/*湿度*/
idata u8 humidity;//电压转换湿度

void ADProc()
{
   float RB2 = ADRead() / 51.0;//读取RB2的电压
   //湿度转换
   if(RB2 <= 1.0)
   	humidity = 10;
   else if(RB2 >= 4.0)
   	humidity = 90;
   else
   	humidity = 80/3.0*RB2+10-80/3.0;
}
  • void SegProc()
    由于数码管缓存区初始化时设置为8个数码管均熄灭,所以只需对索引为0,6,7的数码管赋值即可。
idata u8 SegPos;
pdata u8 SegBuf[8] = {10,10,10,10,10,10,10,10};

void SegProc()
{
	switch(SegMode)
	{
		case 0:
			SegBuf[0] = 11;//C
			SegBuf[6] = humidity / 10 ? humidity / 10 : 10;
			SegBuf[7] = humidity % 10;
		break;
	}
}

void Timer0_Isr(void) interrupt 1
{
	if(++SegPos == 8)	
		SegPos = 0;
	SegDisp(SegPos, SegBuf[SegPos]);
}

2.页面2

页面2要显示的格式是:
最左边的数码管显示字母E,最后两位显示湿度参数的十位和个位(不足两位十位熄灭)

湿度参数默认值为50

01234567
E50

这个就特别简单了,页面1的代码直接复制下来就可以了,唯一要改的地方就是页面2的显示要对湿度参数的十位进行判断,为0时熄灭。

idata u8 humidity_set = 50;

void SegProc()
{
	switch(SegMode)
	{
		case 1:
			SegBuf[0] = 12;//E
			SegBuf[6] = humidity_set / 10 ? humidity_set / 10 : 10;
			SegBuf[7] = humidity_set % 10;
		break;
	}
}

3. 页面3

页面3要显示的格式是:
最左边的数码管显示字母H,最后两位显示时间间隔的十位和个位(不足两位十位熄灭)

时间间隔默认值为3。
时间间隔是继电器点亮的间隔,跟数码管没关系,直接显示即可。

01234567
E3
idata u8 time = 3;//时间间隔

void SegProc()
{
	switch(SegMode)
	{
		case 2:
			SegBuf[0] = 13;//H
			SegBuf[6] = time / 10 ? time / 10 : 10;
			SegBuf[7] = time % 10;
		break;
	}
}

二、按键部分

按键只用到了按键S4、S5、S8、S9。
按键S4是切换页面,实现也很简单。
按键5是继电器启动标志位,在任何界面按一次标志位取反。
在这里插入图片描述

idata bit relay_work; //继电器工作 0-停止 1-开始

void KeyProc()
{
	KeyVal = KeyDisp();
	KeyDown = KeyVal & ~KeyOld;
	KeyUp = ~KeyVal & KeyOld;
	KeyOld = KeyVal;
	
	switch(KeyDown)
	{
		case 4://页面流转
			if(++SegMode == 3)
				SegMode = 0;
		break;
			
		case 5://切换继电器工作模式
			relay_work = !relay_work;
		break;
		
		case 8://参数-
			if(SegMode == 1)
			{
				humidity_set -= 5;
				if(humidity_set == 25)
					humidity_set = 90;
			}
			else if(SegMode == 2)
			{
				if(--time == 0)
					time = 10;
			}
		break;
			
		case 9://参数+
			if(SegMode == 1)
			{
				humidity_set += 5;
				if(humidity_set == 95)
					humidity_set = 30;
			}
			else if(SegMode == 2)
			{
				if(++time == 11)
					time = 1;
			}
		break;
	}
}

三、继电器部分

第一次写继电器的时候理解错了,题目也没有明确提到,根据第一次提交的测评分析这道题目的意思是继电器工作模式下触发吸合条件后继电器吸合,经过一定时间间隔后断开,持续处于该状态不重复吸合,直到经过一次湿度>湿度参数后再次触发条件(湿度<湿度参数),继电器才吸合。

idata bit relay_work; //继电器工作 0-停止 1-开始
idata bit relay_flag; //继电器使能标志位
idata u16 TimeCount;  //定时器计时变量 
idata bit humidity_flag; //0-湿度值不小于参数 
idata bit humidity_has_flag;//0-继电器未重复吸合 1-继电器已重复吸合过
void LedProc()
{
	/*Relay*/
	if(!relay_work)//继电器不工作
	{
		relay_flag = 0;//关闭继电器
		humidity_has_flag = 0;//继电器重复吸合标志位清零
	}
	else//继电器工作
	{
		humidity_flag = (humidity < humidity_set);//如果湿度小于湿度参数,humidity_flag为真
		if(!humidity_flag)//如果湿度大于湿度参数
			humidity_has_flag = 0;//重复吸合标志位清零
		if(humidity_flag && !humidity_has_flag)//如果湿度小于湿度参数并且还未触发继电器吸合
		{
			humidity_has_flag = 1;//继电器已经吸合过了
			relay_flag = 1;//继电器开始工作
		}
	}
	Relay(relay_flag);
}

void Timer0_Isr(void) interrupt 1
{
	//如果处于继电器工作模式下并且继电器开始工作时计时
	if(relay_work && relay_flag)
	{
		if(++TimeCount == time*1000)//吸合后达到一定时间间隔断开继电器
		{
			TimeCount = 0;//计时变量清零
			relay_flag = 0;//继电器不工作
		}
	}
}

四、代码整合(改一下main函数可以运行)

Init.c

#include <STC15F2K60S2.H>

void SystemInit()
{
	P0 = 0xff;
	P2 = P2 & 0x1f | 0x80;
	P2 &= 0x1f;
	
	P0 = 0x00;
	P2 = P2 & 0x1f | 0xa0;
	P2 &= 0x1f;
}

Led.h

#include <STC15F2K60S2.H>

void LedDisp(unsigned char *ucLed)
{
	unsigned char i, temp = 0x00;
	static unsigned char temp_old = 0xff;
	
	for(i = 0; i < 8; i++)
		temp |= (ucLed[i] << i);
	
	if(temp != temp_old)
	{
		P0 = ~temp;
		P2 = P2 & 0x1f | 0x80;
		P2 &= 0x1f;
		temp_old = temp;
	}
}

void Relay(bit flag)
{
	unsigned char temp = 0x00;
	static unsigned char temp_old = 0xff;
	
	if(flag)
		temp |= 0x10;
	else
		temp &= 0xef;
	
	if(temp != temp_old)
	{
		P0 = temp;
		P2 = P2 & 0x1f | 0xa0;
		P2 &= 0x1f;
		temp_old = temp;
	}
}

Key.h

#include <STC15F2K60S2.H>

unsigned char KeyDisp()
{
	unsigned char temp = 0x00;
	
	P44 = 0;
	P42 = 1;
	P35 = 1;
	P34 = 1;
	if(P32 == 0) temp = 5;
	if(P33 == 0) temp = 4;
	
	P44 = 1;
	P42 = 0;
	P35 = 1;
	P34 = 1;
	if(P32 == 0) temp = 9;
	if(P33 == 0) temp = 8;
	
	return temp;
}

Seg.c

#include <STC15F2K60S2.H>

code unsigned char Seg_Table[] =
{
0xc0, //0
0xf9, //1
0xa4, //2
0xb0, //3
0x99, //4
0x92, //5
0x82, //6
0xf8, //7
0x80, //8
0x90, //9
0xff, //空
0xc6, //C
0x86, //E
0x89  //H
};

void SegDisp(unsigned char wela, unsigned char dula)
{
	P0 = 0xff;
	P2 = P2 & 0x1f | 0xe0;
	P2 &= 0x1f;
	
	P0 = (0x01 << wela);
	P2 = P2 & 0x1f | 0xc0;
	P2 &= 0x1f;
	
	P0 = Seg_Table[dula];
	P2 = P2 & 0x1f | 0xe0;
	P2 &= 0x1f;
}

pcf8591.c

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

#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 ADRead()
{
	unsigned char temp = 0;
	I2CStart();
	I2CSendByte(0x90);
	I2CWaitAck();
	I2CSendByte(0x03);
	I2CWaitAck();
	
	I2CStart();
	I2CSendByte(0x91);
	I2CWaitAck();
	temp = I2CReceiveByte();
	I2CSendAck(1);
	I2CStop();
	
	return temp;
}

main.c

#include <STC15F2K60S2.H>
#include "Init.h"
#include "Led.h"
#include "Key.h"
#include "Seg.h"
#include "pcf8591.h"

typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long int u32;

/*按键*/
idata u8 KeyVal, KeyDown, KeyUp, KeyOld;
/*数码管*/
idata u8 SegPos;
pdata u8 SegBuf[8] = {10,10,10,10,10,10,10,10};
idata u8 SegMode;
/*指示灯*/
pdata u8 ucLed[8] = {0,0,0,0,0,0,0,0};
idata bit relay_work; //继电器工作 0-停止 1-开始
idata bit relay_flag; //继电器使能标志位
idata u16 TimeCount;  //定时器计时变量 
/*湿度*/
idata u8 humidity;
idata u8 humidity_set = 50;
idata u8 time = 3;
idata bit humidity_flag; //0-湿度值不小于参数 
idata bit humidity_has_flag; 

void KeyProc()
{
	KeyVal = KeyDisp();
	KeyDown = KeyVal & ~KeyOld;
	KeyUp = ~KeyVal & KeyOld;
	KeyOld = KeyVal;
	
	switch(KeyDown)
	{
		case 4:
			if(++SegMode == 3)
				SegMode = 0;
		break;
			
		case 5:
			relay_work = !relay_work;
		break;
		
		case 8:
			if(SegMode == 1)
			{
				humidity_set -= 5;
				if(humidity_set == 25)
					humidity_set = 90;
			}
			else if(SegMode == 2)
			{
				if(--time == 0)
					time = 10;
			}
		break;
			
		case 9:
			if(SegMode == 1)
			{
				humidity_set += 5;
				if(humidity_set == 95)
					humidity_set = 30;
			}
			else if(SegMode == 2)
			{
				if(++time == 11)
					time = 1;
			}
		break;
	}
}

void SegProc()
{
	switch(SegMode)
	{
		case 0:
			SegBuf[0] = 11;//C
			SegBuf[6] = humidity / 10 ? humidity / 10 : 10;
			SegBuf[7] = humidity % 10;
		break;
		
		case 1:
			SegBuf[0] = 12;//E
			SegBuf[6] = humidity_set / 10 ? humidity_set / 10 : 10;
			SegBuf[7] = humidity_set % 10;
		break;
		
		case 2:
			SegBuf[0] = 13;//H
			SegBuf[6] = time / 10 ? time / 10 : 10;
			SegBuf[7] = time % 10;
		break;
	}
}

void LedProc()
{
	u8 i;
	/*Relay*/
	if(!relay_work)
	{
		relay_flag = 0;
		humidity_has_flag = 0;
	}
	else
	{
		humidity_flag = (humidity < humidity_set);
		if(!humidity_flag)
			humidity_has_flag = 0;
		if(humidity_flag && !humidity_has_flag)
		{
			humidity_has_flag = 1;
			relay_flag = 1;
		}
	}
	Relay(relay_flag);
	
	/*Led*/
	for(i = 0; i < 3; i++)
		ucLed[i] = (i == SegMode);
	ucLed[3] = relay_work;
	LedDisp(ucLed);
}

void ADProc()
{
	float RB2 = ADRead() / 51.0;
	if(RB2 <= 1.0)
		humidity = 10;
	else if(RB2 >= 4.0)
		humidity = 90;
	else
		humidity = 80/3.0*RB2+10-80/3.0;
}

void Timer0_Init(void)		//1毫秒@12.000MHz
{
	AUXR &= 0x7F;			//定时器时钟12T模式
	TMOD &= 0xF0;			//设置定时器模式
	TL0 = 0x18;				//设置定时初始值
	TH0 = 0xFC;				//设置定时初始值
	TF0 = 0;				//清除TF0标志
	TR0 = 1;				//定时器0开始计时
	ET0 = 1;				//使能定时器0中断
	EA = 1;
}

void Timer0_Isr(void) interrupt 1
{
	if(++SegPos == 8)	SegPos = 0;
	SegDisp(SegPos, SegBuf[SegPos]);
	
	if(relay_work && relay_flag)
	{
		if(++TimeCount == time*1000)
		{
			TimeCount = 0;
			relay_flag = 0;
		}
	}
}

void main()
{
	SystemInit();
	Timer0_Init();
	while(1)
	{
		//...
	}
}

相关文章:

  • Vue3(自定义指令directive详解)
  • Kubernetes中将SpringBoot3的application.yaml配置文件迁移到ConfigMap实现配置与代码的分离
  • 简述一下Unity中的碰撞检测
  • c# 2025-3-22 周六
  • 优化 SQL 语句方向和提升性能技巧
  • 比特币驱动金融第八章——探索比特币之外:多样化的挖矿算法
  • EtherCAT转ProfiNet网关如何实现西门子1200PLC与伺服电机通讯(ProfiNet总线协议)
  • 【硬核实战】ETCD+AI智能调度深度整合!从架构设计到调优避坑,手把手教你打造高可用调度系统!
  • Cursor安装注册+基础配置+入门实操
  • elasticsearch+sentencetransformer检索样例
  • SpringBoot3+Vue3实战(Vue3快速开发登录注册页面并对接后端接口、表单项自定义校验规则、Hutool工具类)(4)
  • 深度学习复习笔记(8)特征提取与无监督学习
  • 再学:区块链基础与合约初探 EVM与GAS机制
  • LiteraSageAI 项目介绍
  • hexo+butterfly博客功能完善和美化(四)---博客上传
  • Ubuntu实时读取音乐软件的音频流
  • 网络世界探索之旅:网络编程
  • 深入解析组合模式(Composite Pattern):概念、结构与应用
  • 可视化动态表单动态表单界的天花板--Formily(阿里开源)
  • 阿里云国际站代理商:服务器网页如何应对恶意网络爬虫?
  • “拼好假”的年轻人,今年有哪些旅游新玩法?
  • 中国天主教组织发贺电对新教皇当选表示祝贺
  • 印巴战火LIVE丨印巴互相发动无人机袭击,巴官员称两国已在国安层面接触
  • 国家税务总局泰安市税务局:山东泰山啤酒公司欠税超536万元
  • 欧盟公布对美关税反制清单,瞄准美国飞机等产品
  • 中俄元首今年首次面对面会谈,达成哪些新的重要共识?