# 前言 今天花了一两个小时把蓝桥杯的第七届省赛试题给做出来了。感觉这个超级简单。感觉自己离去北京玩又近了一步。哈哈哈! 话不多说,开始今天的正题。 # 试题分析 ## 题目 首先我们先来看看题目。 ### 功能简述 “模拟风扇控制系统”能够模拟电风扇工作,通过按键控制风扇的转动速度和定时时间,数码管实时显示风扇的工作模式,动态倒计时显示剩余的定时时间,系统主要由数码管显示、单片机最小系统、按键输入和电机控制保护电路组成,系统框图如图 1 所示: ![90CD1964E1AF2492E154265CBA0CCED6.jpg][1] ### 设计任务及要求 #### 1. 工作模式 设备具有“睡眠风”、“自然风”和“常风”三种工作模式可以通过按键切换,通过单片机 P34 引脚输出脉宽调制信号控制电机运行状态,信号频率为 1KHz。 1.1 “睡眠风”模式下,对应 PWM 占空比为 20%; 1.2 “自然风”模式下,对应 PWM 占空比为 30%; 1.3 “常风”模式下,对应 PWM 占空比为 70%; #### 2. 数码管显示 数码管实时显示设备当前工作模式和剩余工作时间(倒计时),如图 2 所示。 ![5110739653817C5D7B9E870ED425CB85.jpg][2] “睡眠风”状态下,对应数码管显示数值为 1,自然风模式下,显示数值为 2,常风模式下,显示数值为 3。 #### 3. 按键控制 使用 S4、S5、S6、S7 四个按键完成按键控制功能。 2.1 按键S4定义为工作模式切换按键,每次按下S4,设备循环切换三种工作模式。 工作过程如下: ![9AAD11E4F0AD307584EEF98F11D95A70.jpg][3] 2.2 按键 S5 定义为“定时按键”每次按下 S5,定时时间增加 1 分钟,设备的剩余工作时间重置为当前定时时间,重新开始倒计时,工作过程如下: ![FA7705DFA1907D1BC8EAEA1E95C90D37.jpg][4] 设备剩余工作时间为 0 时,停止 PWM 信号输出。 2.3 按键 S6 定义为“停止”按键,按下 S6 按键,立即清零剩余工作时间,PWM信号停止输出,直到通过 S5 重新设置定时时间。 2.4 按键 S7 定义为“室温”按键,按下 S7,通过数码管显示当前室温,数码管显示格式如图 3 所示,再次按下 S7,返回图 2 所示的工作模式和剩余工作时间显示界面,如此往复。 ![6933F3D0BAB5FC553BAAB5F110DFD045.jpg][5] 室温测量、显示功能不应影响设备正在执行的 PWM 信号输出、停止、模式切换和计时等功能。 #### 4. LED 指示灯 “睡眠风”模式下,L1 点亮,“自然风”模式下 L2 点亮,“常风”模式下 L3 点亮;按下停止按键或倒计时结束时,LED 全部熄灭。 ## 分析 OK。第七届的试题已经在上面呈现了,接下来我们来分析一下题目。 首先有他给出的拓扑图得出,我们需要用到的模块有:MUC、数码管显示模块、LED指示灯模块、DS18B20模块、PWM输出模块。 这些模块中,可能最难的就是PWM输出模块了。所以我还是提前去看了一下资料的,才能这么快的把程序写出来! 有关PWM的资料请看我下面这篇文章: 基于51单片机的PWM控制流水灯 概述首先我们先介绍一下PWM是什么。PWM也叫脉冲宽度调制,是一种模拟控制方式,根据相应载荷的变化来调制晶体管基极... 其实这个也不难,通俗来讲就是在一个规定的时间内占空比是多少。 然后下面一个是DS18B20的模块。同时也可以借鉴我下面这篇文章: 蓝桥杯-单片机经验分享-18B20 前言经过几天对107D开发版上的18B20的琢磨,终于在今天顺利的让他正确的显示了温度。下面写一些在这过程中遇到的... 至于其他的模块我就不用多说了。 # 实现过程 现在我们来讲一下实现的过程。 ## Step.1 首先我们要做一些初始化。 ### 关闭与本题无关的模块 这里主要是蜂鸣器、继电器和LED灯了,代码如下: ``` LED_data = 0xff; //这里我所用的是寄存器寻址的方法 buzz_data = 0x00; ``` ### 初始化定时器 这里我们用到了两个定时器,定时器0和定时器1。代码如下: ``` //定时器初始化 void Timer1Init(void) //100微秒@12.000MHz { AUXR |= 0x40; //定时器时钟1T模式 TMOD &= 0x0F; //设置定时器模式 TL1 = 0x50; //设置定时初值 TH1 = 0xFB; //设置定时初值 TF1 = 0; //清除TF1标志 TR1 = 1; //定时器1开始计时 EA = 1; //开总中断 ET1 = 1; //开定时器1中断 ET0 = 1; //开定时器0中断 TR0 = 1; //定时器1开始计时 } void Timer0Init(void) //1毫秒@12.000MHz { AUXR |= 0x80; //定时器时钟1T模式 TMOD &= 0xF0; //设置定时器模式 TL0 = 0x20; //设置定时初值 TH0 = 0xD1; //设置定时初值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 } ``` 定时器1我们是用作PWM输出的,然后定时器0就用作状态机和数码管扫描显示的。 ## Step.2 软件上的初始化我们已经做完了,现在就需要看题目需求来做初始化了。 分析题目可知:模式显示为1,然后时间为0秒。所以我们的模式变量等于1,时间变量等于0;然后再用数码管显示出来就没问题了。 ## Step.3 现在就是温度显示,在这里我就不再说了,请下下面这篇文章: 蓝桥杯-单片机经验分享-18B20 前言经过几天对107D开发版上的18B20的琢磨,终于在今天顺利的让他正确的显示了温度。下面写一些在这过程中遇到的... ## Step.4 现在说一下键盘扫描程序。 自从我会了状态机扫描以后,我就没有用过软件消抖这种方式了。状态机真的好用。所以本题我也采用的是状态机的方式来消抖。其实只要理解了就真的不难。 有关内容请看下面这篇文章: 基于51单片机的4x4矩阵键盘的状态机消抖 前言首先,我先给大家说一下按键抖动的原因和原理是什么。原因通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时... ## Step.5 PWM输出,我用的是定时器1,题目要求的频率是1KHz,那么换算过来就是1ms。并且题目中占空比有三种模式,20%、30%以及70%。那么,就相当于1模式内高电平所占的比例,这个很好写,代码如下: ``` void timer0() interrupt 3 { static uchar i = 0; i++; if(mode_time) { if(mode == 1) { LED_data =0xfe; if(i <= 3) { P34 = 1; } else { P34 = 0; } } else if(mode == 2) { LED_data =0xfd; if(i <= 4) { P34 = 1; } else { P34 = 0; } } else { LED_data =0xfb; if(i <= 8) { P34 = 1; } else { P34 = 0; } } if(i >= 11) i = 0; } else LED_data = 0xff; } ``` ## 其他的好像就没得啥好说的了,下面我们直接贴出所有代码,欢迎各位评论! #代码分享 #####onewire.c ``` /* 程序说明: 单总线驱动程序 软件环境: Keil uVision 4.10 硬件环境: CT107单片机综合实训平台(外部晶振12MHz) STC89C52RC单片机 日 期: 2011-8-9 */ #include "reg52.h" sbit DQ = P1^4; //单总线接口 //单总线延时函数 void Delay_OneWire(unsigned int t) //STC89C52RC { while(t--); } //通过单总线向DS18B20写一个字节 void Write_DS18B20(unsigned char dat) { unsigned char i; for(i=0;i<8;i++) { DQ = 0; DQ = dat&0x01; Delay_OneWire(50); DQ = 1; dat >>= 1; } Delay_OneWire(50); } //从DS18B20读取一个字节 unsigned char Read_DS18B20(void) { unsigned char i; unsigned char dat; for(i=0;i<8;i++) { DQ = 0; dat >>= 1; DQ = 1; if(DQ) { dat |= 0x80; } Delay_OneWire(50); } return dat; } //DS18B20设备初始化 bit init_ds18b20(void) { bit initflag = 0; DQ = 1; Delay_OneWire(120); DQ = 0; Delay_OneWire(800); DQ = 1; Delay_OneWire(100); initflag = DQ; Delay_OneWire(50); return initflag; } unsigned char rd_temperature() { unsigned char low,hight,temperature; init_ds18b20(); Write_DS18B20(0xCC); Write_DS18B20(0x44); Delay_OneWire(200); init_ds18b20(); Write_DS18B20(0xCC); Write_DS18B20(0xBE); low = Read_DS18B20(); hight = Read_DS18B20(); temperature = hight << 4; temperature |= low >> 4; return temperature; } ``` ##### onewire.h ``` #ifndef __ONEWIRE_H #define __ONEWIRE_H unsigned char rd_temperature(void); //; ; #endif ``` ##### 主程序 main.c ``` #include #include #include #include #define uint unsigned int #define uchar unsigned char #define smg_duan_data XBYTE[0xe000] #define smg_wei_data XBYTE[0xc000] #define buzz_data XBYTE[0xa000] #define LED_data XBYTE[0x8000] sbit P34 = P3^4; //数码管段码暂存 uchar code smg_duan[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff}; //数码管位码暂存 uchar code smg_wei[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; //数码管显示缓存 uchar smg_count[]={0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}; uchar mode = 1,mode_tem = 0; //PWM输出模式 uchar flag_S4 = 0,flag_S5 = 0,flag_S6 = 0,flag_S7 = 0; uint mode_time = 0; //定时器初始化 void Timer1Init(void) //100微秒@12.000MHz { AUXR |= 0x40; //定时器时钟1T模式 TMOD &= 0x0F; //设置定时器模式 TL1 = 0x50; //设置定时初值 TH1 = 0xFB; //设置定时初值 TF1 = 0; //清除TF1标志 TR1 = 1; //定时器1开始计时 EA = 1; //开总中断 ET1 = 1; //开定时器1中断 ET0 = 1; //开定时器0中断 } void Timer0Init(void) //1毫秒@12.000MHz { AUXR |= 0x80; //定时器时钟1T模式 TMOD &= 0xF0; //设置定时器模式 TL0 = 0x20; //设置定时初值 TH0 = 0xD1; //设置定时初值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 } //数码管扫描 void smg_display() { static uchar i = 0; smg_duan_data = 0xff; smg_wei_data = smg_wei[i]; smg_duan_data = smg_count[i]; if(++i == 8) { i = 0; } } //键盘扫描程序 void key_scan() { static uchar i = 0; uchar x = P3; x |= 0xf0; switch(i) { case 0: { if(x != 0xff) { i = 1; } else i = 0; break; } case 1: { if(x != 0xff) { i = 2; } else { i = 0; break; } } case 2: { switch(x) { case 0xfe: { flag_S7++; if(flag_S7 == 1) { mode_tem = 4; } if(flag_S7 == 2) { mode_tem = 0; flag_S7 = 0; } break; } case 0xfd: { mode_time = 0; break; } case 0xfb: { mode_time += 60; if(mode_time >= 180) { mode_time = 0; } break; } case 0xf7: { mode++; if(mode == 4) { mode = 1; } break; } } i = 3; break; } case 3: { if(x == 0xff) { i = 0; } break; } } } void main() { uchar tem = 0; LED_data = 0xff; buzz_data = 0x00; Timer0Init(); Timer1Init(); TR1 = 1; smg_count[0] = smg_count[2] = 0xbf; while(1) { while(mode_tem == 4) { tem = rd_temperature(); smg_count[4] = 0xff; // smg_count[5] = smg_duan[tem >> 4]; // smg_count[6] = smg_duan[tem & 0x0f]; smg_count[5] = smg_duan[tem/10]; smg_count[6] = smg_duan[tem%10]; smg_count[7] = 0xc6; smg_count[1] = smg_duan[mode_tem]; } if(mode_time) { TR0 = 1; } else { LED_data = 0xff; TR0 = 0; } if(mode_tem == 0 && mode == 1) { smg_count[1] = smg_duan[mode]; } else if(mode_tem == 0 && mode == 2) { smg_count[1] = smg_duan[mode]; } else { smg_count[1] = smg_duan[mode]; } if(!mode_tem) { smg_count[4] = smg_duan[mode_time % 10000 / 1000]; smg_count[5] = smg_duan[mode_time % 1000 / 100]; smg_count[6] = smg_duan[mode_time % 100 / 10]; smg_count[7] = smg_duan[mode_time % 10]; } } } //定时器1服务函数 用作PWM输出 void timer0() interrupt 3 { static uchar i = 0; i++; if(mode_time) { if(mode == 1) { LED_data =0xfe; if(i <= 3) { P34 = 1; } else { P34 = 0; } } else if(mode == 2) { LED_data =0xfd; if(i <= 4) { P34 = 1; } else { P34 = 0; } } else { LED_data =0xfb; if(i <= 8) { P34 = 1; } else { P34 = 0; } } if(i >= 11) i = 0; } else LED_data = 0xff; } //定时器0服务函数 void timer1() interrupt 1 { static uchar i = 0; static uint x = 0; if(mode_time) x ++; i ++; if(x >= 1000) { mode_time --; x = 0; } if(i == 8) { P3 = P3 | 0x0f; key_scan(); i = 0; } smg_display(); } ``` # 好啦,今天的文章到此结束,欢迎各位大佬提问!共同学习! #**武汉加油!** #**中国加油!** [1]: https://www.qingfengblog.com/usr/uploads/2020/03/1390058600.jpg [2]: https://www.qingfengblog.com/usr/uploads/2020/03/154059406.jpg [3]: https://www.qingfengblog.com/usr/uploads/2020/03/4132949821.jpg [4]: https://www.qingfengblog.com/usr/uploads/2020/03/3561362763.jpg [5]: https://www.qingfengblog.com/usr/uploads/2020/03/112431172.jpg Last modification:March 8, 2020 © Allow specification reprint Support Appreciate the author AliPayWeChat Like 0 如果觉得我的文章对你有用,请随意赞赏