前言

今天花了一两个小时把蓝桥杯的第七届省赛试题给做出来了。感觉这个超级简单。感觉自己离去北京玩又近了一步。哈哈哈!
话不多说,开始今天的正题。

试题分析

题目

首先我们先来看看题目。

功能简述

“模拟风扇控制系统”能够模拟电风扇工作,通过按键控制风扇的转动速度和定时时间,数码管实时显示风扇的工作模式,动态倒计时显示剩余的定时时间,系统主要由数码管显示、单片机最小系统、按键输入和电机控制保护电路组成,系统框图如图 1 所示:
90CD1964E1AF2492E154265CBA0CCED6.jpg

设计任务及要求

1. 工作模式

设备具有“睡眠风”、“自然风”和“常风”三种工作模式可以通过按键切换,通过单片机 P34 引脚输出脉宽调制信号控制电机运行状态,信号频率为 1KHz。
1.1 “睡眠风”模式下,对应 PWM 占空比为 20%;
1.2 “自然风”模式下,对应 PWM 占空比为 30%;
1.3 “常风”模式下,对应 PWM 占空比为 70%;

2. 数码管显示

数码管实时显示设备当前工作模式和剩余工作时间(倒计时),如图 2 所示。

5110739653817C5D7B9E870ED425CB85.jpg

“睡眠风”状态下,对应数码管显示数值为 1,自然风模式下,显示数值为 2,常风模式下,显示数值为 3。

3. 按键控制

使用 S4、S5、S6、S7 四个按键完成按键控制功能。
2.1 按键S4定义为工作模式切换按键,每次按下S4,设备循环切换三种工作模式。
工作过程如下:
9AAD11E4F0AD307584EEF98F11D95A70.jpg
2.2 按键 S5 定义为“定时按键”每次按下 S5,定时时间增加 1 分钟,设备的剩余工作时间重置为当前定时时间,重新开始倒计时,工作过程如下:
FA7705DFA1907D1BC8EAEA1E95C90D37.jpg
设备剩余工作时间为 0 时,停止 PWM 信号输出。
2.3 按键 S6 定义为“停止”按键,按下 S6 按键,立即清零剩余工作时间,PWM信号停止输出,直到通过 S5 重新设置定时时间。
2.4 按键 S7 定义为“室温”按键,按下 S7,通过数码管显示当前室温,数码管显示格式如图 3 所示,再次按下 S7,返回图 2 所示的工作模式和剩余工作时间显示界面,如此往复。
6933F3D0BAB5FC553BAAB5F110DFD045.jpg
室温测量、显示功能不应影响设备正在执行的 PWM 信号输出、停止、模式切换和计时等功能。

4. LED 指示灯

“睡眠风”模式下,L1 点亮,“自然风”模式下 L2 点亮,“常风”模式下 L3 点亮;按下停止按键或倒计时结束时,LED 全部熄灭。

分析

OK。第七届的试题已经在上面呈现了,接下来我们来分析一下题目。
首先有他给出的拓扑图得出,我们需要用到的模块有:MUC、数码管显示模块、LED指示灯模块、DS18B20模块、PWM输出模块。
这些模块中,可能最难的就是PWM输出模块了。所以我还是提前去看了一下资料的,才能这么快的把程序写出来!
有关PWM的资料请看我下面这篇文章:

其实这个也不难,通俗来讲就是在一个规定的时间内占空比是多少。

然后下面一个是DS18B20的模块。同时也可以借鉴我下面这篇文章:


至于其他的模块我就不用多说了。

实现过程

现在我们来讲一下实现的过程。

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

现在就是温度显示,在这里我就不再说了,请下下面这篇文章:

Step.4

现在说一下键盘扫描程序。
自从我会了状态机扫描以后,我就没有用过软件消抖这种方式了。状态机真的好用。所以本题我也采用的是状态机的方式来消抖。其实只要理解了就真的不难。
有关内容请看下面这篇文章:

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 <reg52.h>
#include <intrins.h>
#include <onewire.h>
#include <absacc.h>

#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();
}

好啦,今天的文章到此结束,欢迎各位大佬提问!共同学习!

武汉加油!

中国加油!

Last modification:March 8th, 2020 at 09:40 pm
如果觉得我的文章对你有用,请随意赞赏