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

第十六届蓝桥杯单片机组省赛(第一套)

看到很多人在问第十六届蓝桥杯单片机难不难,以及实现多少功能可以获得省一。

  • 先介绍下我的作答情况吧,选择题只对一题,程序题的求连续两次距离差值没有考虑负数的情况,其他功能都实现了,成绩是福建省省一第一页,感谢4T老爷手下留情。
  • 至于第十六届蓝桥杯单片机组的难度呢?我认为比十五届难(主要难在阅读能力与排坑能力),比十四届简单(毕竟十四届是真神)。
  • 以下代码仅供参考,不能保证完全正确。

附件:第十六届蓝桥杯单片机组省赛第一次
在这里插入图片描述
在这里插入图片描述

一、模板搭建

在这里插入图片描述
本题的页面流转要实现四个页面,并且参数设置时参数是不生效的,所以在进入参数设置时要再定义一个新的变量去代替原来的参数进行修改,退出参数设置时再保存修改后的值。
在这里插入图片描述
至此,可以先搭建以下模板:

  • main.c
#include <STC15F2K60S2.H>
#include "ds18b20.h"
#include "wave.h"
#include "init.h"
#include "led.h"
#include "key.h"
#include "seg.h"
#include "iic.h"typedef unsigned char u8;
typedef unsigned int u16;/*按键*/
idata u8 keyVal,keyDown,keyUp,keyOld;
/*数码管*/
idata u8 segPos;
pdata u8 segBuf[8] = {10,10,10,10,10,10,10,10};
idata u8 segMode;   //0-环境 1-运动 2-参数 3-统计
/*指示灯*/
pdata u8 ucLed[8] = {0,0,0,0,0,0,0,0};
/*温度*/
idata u8 tem;  				//环境温度
/*AD*/
idata u8 light; 	  //光敏电阻放大10倍
/*超声波*/
idata u8 distance; 					//距离数据(1s采集1次)
/*参数*/
idata u8 keySlow;
idata u8 segSlow;
idata u8 iicSlow;
idata u8 temSlow;
idata u8 waveSlow;
idata u8 setDis[2] = {30,30}; //温度参数/距离参数控制值
idata u8 setCtr[2] = {30,30}; //温度参数/距离参数改变值
idata bit setCtrMode;         //温度参数/距离参数改变值索引void keyProc()
{if(keySlow) return;keySlow = 1;keyVal = keyDisp();keyDown = keyVal & ~keyOld;keyUp = ~keyVal & keyOld;keyOld = keyVal;switch(keyDown){case 4:if(++segMode == 4)segMode = 0;//退出参数设置时保存修改值if(segMode == 3){setCtrMode = 0;setDis[0] = setCtr[0];setDis[1] = setCtr[1];}break;case 5://参数设置子页面切换if(segMode == 2)setCtrMode = !setCtrMode;break;case 8://参数加 30~80 break;case 9://参数加 30~80 break;}
}void segProc()
{if(segSlow) return;segSlow = 1;switch(segMode){case 0://...break;case 1://...break;case 2://...break;case 3://...break;}
}void ledProc()
{
}void iicProc()
{if(iicSlow) return;iicSlow = 1;light = lightRead() / 51.0 * 10;
}void waveProc()
{if(waveSlow) return;waveSlow = 1;distancee = waveGet();
}void temProc()
{if(temSlow) return;temSlow = 1;tem = temRead();
}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(++keySlow == 10) keySlow = 0;if(++segSlow == 90) keySlow = 0;if(++waveSlow == 160) keySlow = 0;if(++iicSlow == 160) keySlow = 0;if(++temSlow == 160) keySlow = 0;if(++segPos == 8) segPos = 0;segDisp(segPos, segBuf[segPos]);ledDisp(ucLed);
}void main()
{systemInit();//系统初始化waveInit();temRead();Timer0_Init();while(1){ledProc();keyProc();segProc();iicProc();temProc();waveProc();}
}

二、环境状态页面

在这里插入图片描述
环境状态页面需要处理光强等级,光强等级是通过光敏电阻电压值转换的。
在这里插入图片描述
只需在iicProc()中简单处理即可:
这边为了节省空间,我使用了unsigned char变量来接收光敏电阻电压值,在接收时将其放大10倍,后续转换时都要放大十倍再进行转换。

void iicProc()
{if(iicSlow) return;iicSlow = 1;light = lightRead() / 51.0 * 10;//放大10倍/*光照强度转化*/if(light < 5)		//光照等级4:V<0.5->v*10<5lightLevel = 4;else if(light < 20) //光照等级3:V<2->v*10<20lightLevel = 3;else if(light < 30) //光照等级2:V<3->v*10<30lightLevel = 2;else				//光照等级1:V>=3lightLevel = 1;
}
void segProc()
{if(segSlow) return;segSlow = 1;switch(segMode){case 0:segBuf[0] = 11;  //CsegBuf[1] = tem / 10;  segBuf[2] = tem % 10; segBuf[4] = 10; segBuf[5] = 10; segBuf[6] = 12;  //nsegBuf[7] = lightLevel;  break;}
}

三、运动检测页面

运动检测页面应该是本题最难的一个页面了,当然这个页面的分数肯定是最多的,因为超声波影响了LED和继电器,一个细节没实现好会导致连坐丢分。
在这里插入图片描述

在这里插入图片描述
运动状态这张图可以帮助理解题意,上电2s内不读取距离数据,直到第2s开始读取第一次距离数据,这个时候运动状态是肯定发生变化了的,所以要锁住运动状态(锁住期间,就算测量距离从12cm变到6cm,运动状态仍然保持第2s的运动状态,直到第5s时才更新运动状态),并且距离数据的测量是有时间间隔的,每1s才测量一次距离数据。
同时还要注意,两次测量数据的差值是要考虑小的数据减大的数据的,这边可以用signed char型变量定义数据差值,也可以用abs,我认为是在相减时先判断哪个数据比较大,然后用大的减小的这种方法比较简单。
要实现的功能很多,碰到这种复杂功能实现,最稳妥的方法就是先实现小功能,再去改进。

1.上电两秒不采集距离数据

上电时,计数变量开始计时两秒,计时期间不进入数据读取(直接return退出),直到两秒是将标志位赋值为1,开始进行数据采集。

idata bit distanceLock;	//刚上电2s时不采集距离数据
idata u16 time2s; 		//计时2svoid waveProc()
{if(waveSlow) return;waveSlow = 1;//上电未满2s直接退出距离处理函数if(!distanceLock)return;
}void Timer0_Isr(void) interrupt 1
{//上电计时2sif(!distanceLock){if(++time2s == 2000)distanceLock = 1;}
}

2.数据采集以一秒为间隔

idata bit distanceFlag;	//距离数据1s采集1次标志位
idata u16 time1s;		//计时变量,1s采集1次数据
idata bit close;        //接近判定标志位void waveProc()
{//采集数据if(!distanceFlag)//保证1s采集1次距离{	distance = waveGet();close = distance < setDis[1]; //距离值低于距离参数触发接近判定distanceFlag = 1;}
}void Timer0_Isr(void) interrupt 1
{//1s采集1次数据if(distanceFlag){if(++time1s == 1000){time1s = 0;distanceFlag = 0;}}
}

3.求相邻两次数据的差值

idata u8 distance; 					//距离数据(1s采集1次)
idata u8 distanceLast;			//上一次的距离数据
idata u8 diff;							//两次距离的差值
void waveProc()
{//...//采集数据if(!distanceFlag)//保证1s采集1次距离{	distance = waveGet();diff = (distance>distanceLast) ? distance-distanceLast : distanceLast-distance;distanceLast = distance;}
}

4.运动状态转换

idata u8 distanceMode = 1;//运动状态(根据diff来判断)
void waveProc()
{//采集数据if(!distanceFlag)//保证1s采集1次距离{	distance = waveGet();close = distance < setDis[1]; //距离值低于距离参数触发接近判定distanceFlag = 1;diff = (distance>distanceLast) ? distance-distanceLast : distanceLast-distance;distanceLast = distance;//差值转换运动状态if(diff < 5)			//静止(L1)distanceMode = 1;else if(diff < 10)//徘徊(L2)distanceMode = 2;else							//运动(L3)distanceMode = 3;}
}

5.运动状态发生变化时锁定运动状态

idata u8 distanceLastMode;//上一次的运动状态
idata bit distanceModeLock;//运动状态锁定
idata u16 time3s;//锁定状态时计时3svoid waveProc()
{if(waveSlow) return;waveSlow = 1;//上电未满2s直接退出距离处理函数if(!distanceLock)return;//采集数据if(!distanceFlag)//保证1s采集1次距离{	distance = waveGet();close = distance < setDis[1]; //距离值低于距离参数触发接近判定distanceFlag = 1;//状态锁定时不判断运动状态变化if(!distanceModeLock){diff = (distance>distanceLast) ? distance-distanceLast : distanceLast-distance;distanceLast = distance;//差值转换运动状态if(diff < 5)			//静止(L1)distanceMode = 1;else if(diff < 10)//徘徊(L2)distanceMode = 2;else							//运动(L3)distanceMode = 3;//两次运动状态不同时锁定运动状态if(distanceLastMode != distanceMode)distanceModeLock = 1;distanceLastMode = distanceMode;//更新运动状态}}
}void Timer0_Isr(void) interrupt 1
{//状态锁定计时3sif(distanceModeLock){if(++time3s == 3000){time3s = 0;distanceModeLock = 0;}}
}
void segProc()
{if(segSlow) return;segSlow = 1;switch(segMode){case 0://... break;case 1:segBuf[0] = 13;  //LsegBuf[1] = distanceMode;  segBuf[2] = 10; segBuf[5] = distance / 100 ? distance / 100 : 0;segBuf[6] = distance / 10 % 10;segBuf[7] = distance % 10;  break;}
}

四、参数设置、统计数据页面

在这里插入图片描述
在这里插入图片描述
当温度高于温度参数,测距数据小于距离参数时,继电器吸合,否则断开,统计数据页面记录的是继电器吸合的次数。

1.keyProc()


void keyProc()
{if(keySlow) return;keySlow = 1;keyVal = keyDisp();keyDown = keyVal & ~keyOld;keyUp = ~keyVal & keyOld;keyOld = keyVal;switch(keyDown){case 4:if(++segMode == 4)segMode = 0;if(segMode == 3){setCtrMode = 0;setDis[0] = setCtr[0];setDis[1] = setCtr[1];}break;case 5:if(segMode == 2)setCtrMode = !setCtrMode;break;case 8://参数加 30~80 if(segMode == 2){if(!setCtrMode)//温度+1{if(++setCtr[0] == 81)setCtr[0] = 80;}else//距离+5{setCtr[1] += 5;if(setCtr[1] == 85)setCtr[1] = 80;}} break;case 9://参数加 30~80 if(segMode == 2){if(!setCtrMode)//温度-1{if(--setCtr[0] == 19)setCtr[0] = 20;}else//距离-5{setCtr[1] -= 5;if(setCtr[1] == 15)setCtr[1] = 20;}} break;}
}

2.segProc()

void segProc()
{if(segSlow) return;segSlow = 1;switch(segMode){case 2:segBuf[0] = 14;  //PsegBuf[1] = !setCtrMode ? 11 : 13;  //C/L segBuf[2] = 10; segBuf[5] = 10;segBuf[6] = setCtr[setCtrMode] / 10;segBuf[7] = setCtr[setCtrMode] % 10; break;case 3:segBuf[0] = 12;  //nsegBuf[1] = 11;  //CsegBuf[4] = relayCount / 1000 ? relayCount / 1000 : 10; segBuf[5] = (segBuf[4]==10&&relayCount/100%10==0) ? 10 : relayCount/100%10;segBuf[6] = (segBuf[5]==10&&relayCount/10%10==0) ? 10 : relayCount/10%10;segBuf[7] = relayCount % 10; break;}
}

五、LED、继电器

1.继电器

idata u16 relayCount; 	//继电器吸合次数
idata bit relayHasCount;//继电器吸合一次只计数一次
void ledProc()
{//上电未满2s直接退出led处理函数if(!distanceLock)return;//继电器temHigFlag = (tem > setDis[0]);if(temHigFlag && close && !relayHasCount){relayDisp(1);relayCount++;relayHasCount = 1;//继电器吸合已经计数了}if(!temHigFlag || !close){relayDisp(0);relayHasCount = 0;//重置计数标志位}
}

2.LED

在这里插入图片描述

/*指示灯*/
pdata u8 ucLed[8] = {0,0,0,0,0,0,0,0};
idata u8 time100ms;  		//计时100msL8闪烁
idata bit ledFlash;  		//L8闪烁标志位void ledProc()
{//L1~L4if(close){switch(lightLevel){case 1:ucLed[0] = 1;ucLed[1] = ucLed[2] = ucLed[3] = 0;break;case 2:ucLed[0] = ucLed[1] = 1;ucLed[2] = ucLed[3] = 0;break;case 3:ucLed[0] = ucLed[1] = ucLed[2] = 1;ucLed[3] = 0;break;case 4:ucLed[0] = ucLed[1] = ucLed[2] = ucLed[3] = 1;break;}}elseucLed[0] = ucLed[1] = ucLed[2] = ucLed[3] = 0;//L8if(distanceMode == 1)ucLed[7] = 0;else if(distanceMode == 2)ucLed[7] = 1;else ucLed[7] = ledFlash;
}void Timer0_Isr(void) interrupt 1
{//L8闪烁if(distanceMode == 3){if(++time100ms == 100){time100ms = 0;ledFlash = !ledFlash;}}else{time100ms = 0;ledFlash = 0;}
}

六、双按键功能处理

1.底层修改

在这里插入图片描述
按键8和按键9在同一列,判断按键8是否按下判断的是P33引脚的是否为0,判断按键9是否按下判断的是P32引脚是否为0,所以两个按键同时按下就是P33引脚和P32引脚同时为0,根据这个理论可以修改按键底层。

  • key.c
#include "key.h"unsigned char keyDisp()
{unsigned char temp = 0;P44 = 0;P42 = 1;P35 = 1;P34 = 1;if(P33 == 0) temp = 4;if(P32 == 0) temp = 5;P44 = 1;P42 = 0;P35 = 1;P34 = 1;if(P33 == 0) temp = 8;if(P32 == 0) temp = 9;if(!P32 && !P33) temp = 89;return temp;
}

2.main.c调用

idata bit keyPressFlag;//双按键同时按下标志位
idata u16 time2000ms;  //双按键长按2s计时
void keyProc()
{if(keySlow) return;keySlow = 1;keyVal = keyDisp();keyDown = keyVal & ~keyOld;keyUp = ~keyVal & keyOld;keyOld = keyVal;//双按键处理if(keyDown == 89 && segMode == 3)keyPressFlag = 1;if(time2000ms == 2001) //长按超过2s{relayCount = 0;keyPressFlag = 0;time2000ms = 0;}if(keyUp == 89){keyPressFlag = 0;time2000ms = 0;}
}void Timer0_Isr(void) interrupt 1
{//双按键长按2sif(keyPressFlag){if(++time2000ms >= 2000)time2000ms = 2001;}
}
http://www.xdnf.cn/news/251407.html

相关文章:

  • 解决 3D Gaussian Splatting 中 SIBR 可视化组件报错 uv_mesh.vert 缺失问题【2025最新版!】
  • 基于深度学习的毒蘑菇检测
  • 大学生入学审核系统设计与实现【基于SpringBoot + Vue 前后端分离技术】
  • 精益数据分析(38/126):SaaS模式的流失率计算优化与定价策略案例
  • ubuntu22.04安装显卡驱动与cuda+cuDNN
  • IntelliJ IDEA 使用教程
  • Linux:信号(一)
  • 八闽十三张模块部署测试记录:源码结构拆解与本地运行验证(含常见问题与修复指南)
  • c/c++开发调试工具之gdb
  • 每天学一个 Linux 命令(34):wc
  • DeepSeek R1:强化学习范式的推理强化模型
  • 华为OD机试真题 Java 实现【水库蓄水问题】
  • 【Linux深入浅出】之全连接队列及抓包介绍
  • 供应链算法整理(一)--- 销量预估
  • 云计算-容器云-服务网格Bookinfo
  • 大模型的第一天学习-LM studio的安装和本地大模型搭建
  • 从0开始建立Github个人博客(hugoPaperMod)
  • 见多识广4:Buffer与Cache,神经网络加速器的Buffer
  • A2A Python 教程 - 综合指南
  • 体系结构论文(八十二):A Comprehensive Analysis of Transient Errors on Systolic Arrays
  • 目标检测中的损失函数(三) | SIoU WIoUv1 WIoUv2 WIoUv3
  • 【计算机视觉】三维视觉:Open3D:现代三维数据处理的全栈解决方案
  • [Verilog]跨时钟域数据传输解决方案
  • 【Linux】Petalinux U-Boot
  • 普通IT的股票交易成长史--20250502 突破(1)
  • 虚拟局域网(VLAN)实验(Cisco Packet Tracer)-路由器、交换机的基本配置
  • 2000-2022年上市公司数字经济专利申请数据
  • 使用Vite创建vue3项目
  • linux下抓包工具--tcpdump介绍
  • 2025年- H20-Lc128-240. 搜索二维矩阵 II(矩阵)---java版