蓝桥杯2024/1/28-----第十一届省赛题笔记

题目要求:

1、 基本要求

1.1 使用大赛组委会提供的国信长天单片机竞赛实训平台,完成本试题的程序设计
与调试。
1.2 选手在程序设计与调试过程中,可参考组委会提供的“资源数据包”。
1.3 请注意: 程序编写、调试完成后选手应通过考试系统提交 完整、可编译的 Keil
工程文件 。选手提交的工程文件应是最终版本,要求 Keil 工程文件以准考证
号(8 位数字)命名, 工程文件夹内应包含以准考证号命名的 hex 文件 ,该 hex
文件是成绩评审的依据。不符合以上文件提交要求的作品将被评为零分或者被
酌情扣分。
1.4 请勿上传与作品工程文件无关的其它文件。

2、 竞赛板配置要求

2.1将 IAP15F2K61S2 单片机内部振荡器频率设定为 12MHz。
2.2键盘工作模式跳线 J5 配置为 BTN 按键模式。
2.3扩展方式跳线 J13 配置为 IO 模式。
2.4 请注意 选手需严格按照以上要求配置竞赛板,编写和调试程序 不符合以上
配置要求的作品将被评为零分或者被酌情扣分。

3、 硬件框图

蓝桥杯2024/1/28-----第十一届省赛题笔记_第1张图片

4、 功能描述

4.1基本功能

1) 通过 DS18B20 完成温度数据测量。
2) 通过数码管实现数据和参数界面的显示。
3) 通过按键实现界面切换、参数设置等功能。
4) 通过 LED 指示灯实现状态指示与报警输出功能。
5) 通过 PCF8591 完成 DAC 模拟电压输出功能。

4.2设计要求

1) 温度数据刷新时间:≤0.5 秒
2) 显示界面切换时间:≤0.3 秒
3) DAC 模拟电压输出响应时间:≤0.3 秒
4) 温度参数可调整范围:
温度上限参数 0 ≤ T MAX < 100
温度下限参数 0 ≤ T MIN < 100

4.3显示功能

蓝桥杯2024/1/28-----第十一届省赛题笔记_第2张图片

4.4按键功能

1) 按键功能说明
S4:定义为“界面切换”按键,按下 S4 按键,切换选择数据显示界
面和参数设置界面。
S5:定义为“参数切换”按键,按下 S5 按键,切换选择温度上限参
数 T MAX 和温度下限参数 T MIN
S6:定义为“加”按键,按下 S6 按键,当前选择的温度参数增加 1℃。
S7:定义为“减”按键,按下 S7 按键,当前选择的温度参数减少 1℃。
2) 按键功能设计要求
按键 S6 和按键 S7 的加、减功能仅在参数设置界面有效。
合理设置参数边界范围,防止出现参数越界和逻辑错误。
每次从数据界面切换到参数界面,默认当前选择的参数是温度下限参
数 T MIN
通过 S4 按键,从参数设置界面退出,进入数据显示界面时,需要进行
必要的参数合理性检查(T MAX ≥T MIN );若设置的参数合理,参数生效,
进入数据界面;反之,自动恢复进入参数设置界面前的有效参数,进
入数据界面。

4.5DAC 输出功能

1) 当前温度 T > T MAX 时,控制 DAC 输出 4.0V。
2) 当前温度 T MIN ≤ T ≤ T MAX 时,控制 DAC 输出 3.0V。
3) 当前温度 T < T MIN 时,控制 DAC 输出 2.0V。

4.6LED 指示灯功能

1) 当前温度满足 T > T MAX ,指示灯 L1 点亮,否则熄灭。
2) 当前温度满足 T MIN ≤ T ≤ T MAX ,指示灯 L2 点亮,否则熄灭。
3) 当前温度满足 T < T MIN ,指示灯 L3 点亮,否则熄灭。
4) 如出现错误的参数设置操作,指示灯 L4 点亮,直至下一次正确的参数设
置后,指示灯熄灭。

4.7初始状态说明

1) 初始状态上电默认处于数据显示界面。
2) 默认参数
温度上限参数 T MAX = 30℃
温度下限参数 T MIN = 20℃

每次建好工程文件夹,里边包含User(放工程文件,mian.c,可以在这里写如同我这个文章的文本文档)、Driver(存放底层文件如Led.c,Led.h等)
新建的工程先搭建框架,可以先书写底层函数(此次书写了六个函数并包含相应的头文件共十二个底层文件)


底层函数内容:

1.初始化底层驱动专用文件

比如先用3个IO口控制74HC138译码器,控制Y4为低电平;当Y4为低电平时,或非门74HC02控制Y4C为高电平,使74HC573的OE端口有效,OE端口有效时,可使用P0口控制LED的亮灭。
可以去多了解74HC138译码器,74HC02或非门,74HC573八路输出透明锁存器的相关内容会更好理解
#include

//关闭外设
void System_Init()
{
    P0 = 0xff;
    P2 = P2 & 0x1f | 0x80;
    P2 &= 0x1f;
    P0 = 0x00;
    P2 = P2 & 0x1f | 0xa0;
    P2 &= 0x1f;
}
//头文件
#include
void System_Init();

2.Led底层驱动专用文件

与初始化底层驱动专用文件同理,需要了解对应的锁存器控制,可以在使用的芯片数据手册查看
#include

void Led_Disp(unsigned char addr,enable)//LED
{
    static unsigned char temp = 0x00;
    static unsigned char temp_Old = 0xff;
    if(enable)
        temp |=0x01 << addr;
    else
        temp&= ~ (0x01 << addr);
    if(temp != temp_Old)
    {
        P0 = ~ temp;
        P2 = P2 & 0x1f | 0x80;
        P2 &= 0x1f;
        temp_Old = temp;
    }
}
void Beep(unsigned char flag)//蜂鸣器
{
    static unsigned char temp = 0x00;
    static unsigned char temp_Old = 0xff;
    if(flag)
        temp |=0x40 ;
    else
        temp &= ~ 0x40 ;
    if(temp != temp_Old)
    {
        P0 = ~ temp;
        P2 = P2 & 0x1f | 0xa0;
        P2 &= 0x1f;
        temp_Old = temp;
    }
}
void Relay(unsigned char flag)//继电器
{
    static unsigned char temp = 0x00;
    static unsigned char temp_Old = 0xff;
    if(flag)
        temp |= 0x10 ;
    else
        temp &= ~ 0x10 ;
    if(temp != temp_Old)
    {
        P0 = ~ temp;
        P2 = P2 & 0x1f | 0xa0;
        P2 &= 0x1f;
        temp_Old = temp;
    }
}

//头文件
#include
void Led_Disp(unsigned char addr,enable);

3.按键底层驱动专用文件

(板子上的按键从按键4开始到按键19,可根据实际硬件修改)
#include

unsigned char Key_Read()
{
    unsigned char temp = 0;
    if(P33 == 0)temp = 4;//独立按键
    if(P32 == 0)temp = 5;
    if(P31 == 0)temp = 6;
    if(P30 == 0)temp = 7;
//    P44 = 0; P42 = 1; P35 = 1; P34 = 1;//矩阵按键
//    if(P33 == 0)temp = 4;
//    if(P32 == 0)temp = 5;
//    if(P31 == 0)temp = 6;
//    if(P30 == 0)temp = 7;
//    P44 = 1; P42 = 0; P35 = 1; P34 = 1;
//    if(P33 == 0)temp = 8;
//    if(P32 == 0)temp = 9;
//    if(P31 == 0)temp = 10;
//    if(P30 == 0)temp = 11;
//    P44 = 1; P42 = 1; P35 = 0; P34 = 1;
//    if(P33 == 0)temp = 12;
//    if(P32 == 0)temp = 13;
//    if(P31 == 0)temp = 14;
//    if(P30 == 0)temp = 15;
//    P44 = 1; P42 = 1; P35 = 1; P34 = 0;
//    if(P33 == 0)temp = 16;
//    if(P32 == 0)temp = 17;
//    if(P31 == 0)temp = 18;
//    if(P30 == 0)temp = 19;    
    return temp;
    
}
//头文件
#include

unsigned char Key_Read();

4.数码管底层驱动专用文件

#include

unsigned char Seg_Dula[] ={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0xc6,0x8c};//数码管段码储存数组
unsigned char Seg_Wela[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//数码管位码储存数组

void Seg_Disp(unsigned char wela,dula,point)//数码管显示函数
{
    P0 = 0xff; //
    P2 = P2 & 0x1f |0xe0;
    P2 &= 0x1f;
    P0 = Seg_Wela[wela];
    P2 = P2 & 0x1f |0xc0;
    P2 &= 0x1f;
    P0 = Seg_Dula[dula];
    if(point)
        P0 &= 0x7f;
    P2 = P2 & 0x1f |0xe0;
    P2 &= 0x1f;
}
//头文件
#include

void Seg_Disp(unsigned char wela,dula,point);

5.//DAC底层驱动专用头文件

/*
  程序说明: IIC总线驱动程序
  软件环境: Keil uVision 4.10 
  硬件环境: CT107单片机综合实训平台 8051,12MHz
  日    期: 2011-8-9
*/

#include "iic.h"


#include "intrins.h"

#define DELAY_TIME 5

#define Photo_Res_Channel 0x41
#define Adj_Res_Channel 0x43


//总线引脚定义


sbit SDA = P2^1;  /* 数据线 */
sbit SCL = P2^0;  /* 时钟线 */

void IIC_Delay(unsigned char i)
{
    do{_nop_();}
    while(i--);        
}

//总线启动条件


void IIC_Start(void)
{
    SDA = 1;
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SDA = 0;
    IIC_Delay(DELAY_TIME);
    SCL = 0;    
}

//总线停止条件


void IIC_Stop(void)
{
    SDA = 0;
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SDA = 1;
    IIC_Delay(DELAY_TIME);
}

//发送应答


void IIC_SendAck(bit ackbit)
{
    SCL = 0;
    SDA = ackbit;                      // 0:应答,1:非应答
    IIC_Delay(DELAY_TIME);
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SCL = 0; 
    SDA = 1;
    IIC_Delay(DELAY_TIME);
}

//等待应答


bit IIC_WaitAck(void)
{
    bit ackbit;
    
    SCL  = 1;
    IIC_Delay(DELAY_TIME);
    ackbit = SDA;
    SCL = 0;
    IIC_Delay(DELAY_TIME);
    return ackbit;
}

//通过I2C总线发送数据


void IIC_SendByte(unsigned char byt)
{
    unsigned char i;

    for(i=0; i<8; i++)
    {
        SCL  = 0;
        IIC_Delay(DELAY_TIME);
        if(byt & 0x80) SDA  = 1;
        else SDA  = 0;
        IIC_Delay(DELAY_TIME);
        SCL = 1;
        byt <<= 1;
        IIC_Delay(DELAY_TIME);
    }
    SCL  = 0;  
}

//从I2C总线上接收数据


unsigned char IIC_RecByte(void)
{
    unsigned char i, da;
    for(i=0; i<8; i++)
    {   
        SCL = 1;
    IIC_Delay(DELAY_TIME);
    da <<= 1;
    if(SDA) da |= 1;
    SCL = 0;
    IIC_Delay(DELAY_TIME);
    }
    return da;    
}


//函数名:DAC转换函数


//入口参数:要进行转换的数值
//返回值:无
//函数功能:对入口参数要转换的DA数据进行转换
void Da_Write(unsigned char dat)
{
    IIC_Start();//发送开启信号
    IIC_SendByte(0x90);//选择PCF8591芯片,确定写的模式
    IIC_WaitAck();//等待PCF8591反馈
    IIC_SendByte(0x41);//使能DA转换(随便写通道编号,不影响,主要的功能是使能DA)
    IIC_WaitAck();//等待PCF8591反馈
    IIC_SendByte(dat);//将待转换的数据发送出去
    IIC_WaitAck();//等待PCF8591反馈
    IIC_Stop();//停止发送
}
//头文件
# include "STC15F2K60S2.H"


void IIC_Start(void); 
void IIC_Stop(void);  
bit IIC_WaitAck(void);  
void IIC_SendAck(bit ackbit); 
void IIC_SendByte(unsigned char byt); 
unsigned char IIC_RecByte(void); 
//函数名:DAC转换函数
//入口参数:要进行转换的数值
//返回值:无
//函数功能:对入口参数要转换的DA数据进行转换
void Da_Write(unsigned char dat);

6.//温度底层驱动专用头文件

/*    #     单总线代码片段说明
    1.     本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
    2.     参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
        中对单片机时钟频率的要求,进行代码调试和修改。
*/

#include "onewire.h"

sbit DQ = P1^4;

//单总线内部延时函数


void Delay_OneWire(unsigned int t) //12T 或1T,根据需要修改
{
    t *= 12;//用了温度这句必须写,设置为12T,这句官方不会写,这两句和下面的功能一样
    while(t--);
//    unsigned char i;
//    while(t--)
//        {
//            for(i=0;i<12;i++);
//        }
}

//单总线写操作


void Write_DS18B20(unsigned char dat)//写
{
    unsigned char i;
    for(i=0;i<8;i++)
    {
        DQ = 0;
        DQ = dat&0x01;
        Delay_OneWire(5);
        DQ = 1;
        dat >>= 1;
    }
    Delay_OneWire(5);
}

//单总线读操作


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(5);
    }
    return dat;
}

//DS18B20初始化


bit init_ds18b20(void)//初始化
{
      bit initflag = 0;
      
      DQ = 1;
      Delay_OneWire(12);
      DQ = 0;
      Delay_OneWire(80);
      DQ = 1;
      Delay_OneWire(10); 
    initflag = DQ;     
      Delay_OneWire(5);
  
      return initflag;
}


//函数名:读取温度函数


//入口参数:无
//函数功能:完成温度转换,并返回转换之后的温度数据
float Read_Temperature()
{    
    unsigned char high,low;//返回温度数据的低八位和高八位
    init_ds18b20();//初始化
    Write_DS18B20(0xcc);//跳过ROM
    Write_DS18B20(0x44);//开始温度转换
    init_ds18b20();//初始化
    Write_DS18B20(0xcc);//跳过ROM
    Write_DS18B20(0xbe);//读取温度
    low = Read_DS18B20();//读取低位
    high = Read_DS18B20();//读取高位
    return((high << 8)|low) / 16.0;//返回温度保留后两位精度数据
    
}
//头文件
#ifndef __ONEWIRE_H
#define __ONEWIRE_H

float Read_Temperature();

#endif

工程主函数内容:

1.头文件声明(把需要用到的头文件添加进来)

/*头文件声明区*/
#include //单片机寄存器专用头文件
#include "Init.h"//初始化底层驱动专用头文件
#include "Key.h"//按键底层驱动专用头文件
#include "Led.h"//Led底层驱动专用头文件
#include "Seg.h"//数码管底层驱动专用头文件
#include "onewire.h"//温度底层驱动专用头文件
#include "iic.h"//DAC底层驱动专用头文件

2.变量声明(把需要用到的所有变量现在这里进行声明)

/*变量声明区*/
unsigned char Key_Val,Key_Old,Key_Down,Key_Up;//按键扫描专用变量
unsigned char Seg_Pos;//数码管扫描专用变量
unsigned char Key_Slow_Down;//按键减速变量
unsigned char Seg_Slow_Down;//数码管减速变量
unsigned char Seg_Buf[8] = {10,10,10,10,10,10,10,10};//数码管数据存放数组
unsigned char Seg_Point[8] = {0,0,0,0,0,0,0,0};//数码管小数点数据存放数组
unsigned char ucLed[8] = {0,0,0,0,0,0,0,0};//LED数据存放数组
bit Seg_Disp_Mode;//数码管显示模式 0-数据界面 1-参数界面
unsigned char Temperature;//实时温度数据
unsigned char Temp_Disp[2] = {30,20};//温度变量显示数组
unsigned char Temp_Ctrol[2] = {30,20};//温度变量控制数组
bit Temp_Index = 1;//参数数组指针
bit Error_Flag;//错误设置标志位

3.按键处理函数(在这里编写按键控制的函数)

/*键盘处理函数*/
void Key_Proc()
{
    if(Key_Slow_Down)return;
    Key_Slow_Down = 1;//键盘减速程序
    Key_Val = Key_Read();//实时读取键码值
    Key_Down = Key_Val & (Key_Val ^ Key_Old);//捕捉按键下降沿
    Key_Up = ~Key_Val & (Key_Val ^ Key_Old);//捕捉按键上升沿
    Key_Old = Key_Val;//辅助扫描变量
    
    switch(Key_Down)
    {
        case 4://界面切换
            Seg_Disp_Mode ^= 1;//循环切换数据界面和参数界面
            if(Seg_Disp_Mode == 1)//从数据界面切换到参数界面
            {
                //上下限指针复位
                Temp_Index = 0;
                //将控制值赋值给显示数组 便于修改数据
                Temp_Disp[0] = Temp_Ctrol[0];
                Temp_Disp[1] = Temp_Ctrol[1];
            }
            else//从参数界面切换到数据界面
            {
                if(Temp_Disp[0] >= Temp_Disp[1])
                {
                    //标志位拉低 表示设置正确
                    Error_Flag = 0;
                    //将显示值赋值给控制数组 用于保存数据
                    Temp_Ctrol[0] = Temp_Disp[0];
                    Temp_Ctrol[1] = Temp_Disp[1];
                }
                else
                    Error_Flag = 1;//标志位拉高 表示设置错误
            }
        break;
        case 5://参数切换
            if(Seg_Disp_Mode == 1) //处于参数界面
                Temp_Index ^= 1;//切换参数选中上下限
        break;
        case 6://参数自加
            if(Seg_Disp_Mode == 1)//处于参数界面
            {
                if(++Temp_Disp[Temp_Index] == 100)
                    Temp_Disp[Temp_Index] = 99;//限制上限到99
            }
        break;
        case 7://参数自减
            if(Seg_Disp_Mode == 1)//处于参数界面
            {
                if(--Temp_Disp[Temp_Index] == 255)
                    Temp_Disp[Temp_Index] = 0;//限制下限到0
            }
        break;
    }
}


4.信息处理函数(需要使用到到的函数进行简单的预处理)

/*信息处理函数*/
void Seg_Proc()
{
    if(Seg_Slow_Down)return;
    Seg_Slow_Down = 1;//数码管减速程序
    
    /*信息获取区*/
    Temperature = Read_Temperature();//实时读取温度值
    
    /*数据显示区*/
    if(Seg_Disp_Mode == 0)//处于数据界面
    {
        Seg_Buf[0] = 11;//显示C
        Seg_Buf[3] = 10;
        Seg_Buf[4] = 10;
        Seg_Buf[6] = (unsigned char) Temperature / 10 % 10;
        Seg_Buf[7] = (unsigned char) Temperature % 10;
    }
    else //处于参数界面
    {
        Seg_Buf[0] = 12;//显示P
        Seg_Buf[3] = Temp_Disp[0] / 10 % 10;
        Seg_Buf[4] = Temp_Disp[0] % 10;
        Seg_Buf[6] = Temp_Disp[1] / 10 % 10;
        Seg_Buf[7] = Temp_Disp[1] % 10;
    
    }
    
}

5.其他函数(其他编写的函数,在这里书写会比较方便理解)

/*其他函数*/
void Led_Proc()
{
    unsigned char i;//For循环专用变量
/*LED相关*/
    ucLed[0] = (Temperature > Temp_Ctrol[0]);
    ucLed[1] = (Temperature <= Temp_Ctrol[0] && Temperature >= Temp_Ctrol[1]);
    ucLed[2] = (Temperature < Temp_Ctrol[1]);
    ucLed[3] = Error_Flag;
    /*DAC相关*/
    for(i = 0;i<3;i++)
    {
        if(ucLed[i] == 1)
        {
            Da_Write(51*(4-i));
            break;
        }
    }
}

6.定时器0中断初始化函数

(这个可以使用STC的定时器计算那里生成c代码,后面要自己添加ET0,EA打开中断)
/*定时器0初始化函数*/
void Timer0Init(void)        //1毫秒@12.000MHz
{
    AUXR &= 0x7F;        //定时器时钟12T模式
    TMOD &= 0xF0;        //设置定时器模式
    TL0 = 0x18;        //设置定时初值
    TH0 = 0xFC;        //设置定时初值
    TF0 = 0;        //清除TF0标志
    TR0 = 1;        //定时器0开始计时
    ET0 = 1;        //定时器中断0打开
    EA = 1;          //总中断打开
}

7.定时器0中断服务函数

(为了定时执行特定的任务,如此处设置了定时的时间触发了数码管和LED产生特定反应)
/*定时器中断服务函数*/
void Timer0server()interrupt 1
{
    if(++Key_Slow_Down == 10)Key_Slow_Down = 0;//键盘减速专用
    if(++Seg_Slow_Down == 500)Seg_Slow_Down = 0;//数码管减速专用
    if(++Seg_Pos == 8)Seg_Pos = 0;//数码管显示专用
    Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos],Seg_Point[Seg_Pos]);
    Led_Disp(Seg_Pos,ucLed[Seg_Pos]);
}

/*延时函数*/

void Delay750ms()        //@12.000MHz
{
    unsigned char i, j, k;
    i = 35;
    j = 51;
    k = 182;
    do
    {
        do
        {
            while (--k);
        } while (--j);
    } while (--i);
}

8.主函数Main(调用书写的函数实现所需的相应功能)

/*Main*/
void main()
{
    Read_Temperature();//上电读取一次温度并且延时750MS避免数据出现85,并且延时750ms让这个85不显示
    Delay750ms();
    Sys_Init();
    Timer0Init();
    while(1)
        
    {
         Key_Proc();
         Seg_Proc();
         Led_Proc();
        
    
    }
}

关于DS18B20和PCF8591以及DAC转换等相关资料,可以搜素对应的数据手册查找观看,在其他篇文章里,对应主题的内容也有相应的部分资料可以查阅

你可能感兴趣的:(蓝桥杯笔记,蓝桥杯,笔记,职场和发展,单片机)