蓝桥杯单片机组省赛2018年第九届 实现和总结

一、题目

蓝桥杯单片机组省赛2018年第九届 实现和总结_第1张图片

蓝桥杯单片机组省赛2018年第九届 实现和总结_第2张图片

通过看题目,我们可以分析得出,这一届省赛的题目,主要考察了以下模块:LED、EEPROM的读与写、按键、数码管、PCF8591的AD转换、定时器以及对继电器和蜂鸣器的关闭。我一般拿到赛题,然后就会去分析里面所运用到的模块,然后把各个模块的模板写出来。然后最后像搭积木一样,来实现整体的功能。

二、模板

以下就是我的模板:

2.1 led

首先创立bsp_led.h这个文件,往里写入:

#include"STC15F2K60S2.H"
void Led_Disp(unsigned char ucled);

然后创立bsp_led.c这个文件在里面写入:

/*
*  在对于传入的参数ucled,我们用正常逻辑来表示灯灭与亮,即1表示亮,0表示灭
*/

#include "bsp_led.h"
void Led_Disp(unsigned char ucled)
{
    ucled =~ ucled;   
    P2 = P2 & 0X1F | 0X80;
    P0 = ucled;
    P2 &= 0x1F; 
}

2.2 PCF8591和EEPROM

这两个模块都是接在iic总线上的,对于iic,在比赛过程中,我们是可以拿到它的底层驱动代码的。一共两个文件,bsp_iic.c和bsp_iic.h。我们往里再加入三个函数,以实现我们的模板。

iic.c文件如下:我们往里加入,EEPROM_Read,EEOROM_Write,PCF8591_ADc这三个函数。

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

#include"bsp_iic.h"

//总线引脚定义
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;    
}
//====================一下为我们加入的内容
void EEPROM_Read(unsigned char *EEPROM_String,unsigned char addr,unsigned char num)
{
    IIC_Start();
    IIC_SendByte(0xA0);
    IIC_WaitAck();
    
    IIC_SendByte(addr);
    IIC_WaitAck();
    
    IIC_Start();
    IIC_SendByte(0xA1);
    IIC_WaitAck();
    while(num--)
    {
        *EEPROM_String++=IIC_RecByte();
        if(num) IIC_SendAck(0);
        else IIC_SendAck(1);
    }
    IIC_Stop();
}
void EEPROM_Write(unsigned char *EEPROM_String,unsigned char addr,unsigned char num)
{
    IIC_Start();
    IIC_SendByte(0xA0);
    IIC_WaitAck();
    
    IIC_SendByte(addr);
    IIC_WaitAck();
    	
    while(num--)
    {
        IIC_SendByte(*EEPROM_String++);
        IIC_WaitAck();
        IIC_Delay(200);
    }
    IIC_Stop();
}
    
unsigned char Pcf8591_Adc(unsigned char channel_num_control)
{
    unsigned char temp;
    IIC_Start();
    IIC_SendByte(0x90);
    IIC_WaitAck();
    
    IIC_SendByte(channel_num_control);
    IIC_WaitAck();
    
    IIC_Start();
    IIC_SendByte(0x91);
    IIC_WaitAck();
    
    temp=IIC_RecByte();
    IIC_SendAck(1);
    IIC_Stop();
    return temp;
}

然后在bsp_iic.h 补上这三个函数的定义。

# include "STC15F2K60S2.H"
#include "intrins.h"

#define DELAY_TIME 5

#define Photo_Res_Channel 0x41
#define Adj_Res_Channel 0x43

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); 

unsigned char Pcf8591_Adc(unsigned char channel_num_contrl);
void EEPROM_Write(unsigned char* EEPROM_String, unsigned char addr, unsigned char num);
void EEPROM_Read(unsigned char* EEPROM_String, unsigned char addr, unsigned char num);

2.3 按键的模板

老套路创建一个bsp_key.h的文件,并在里面写入:

#include"STC15F2K60S2.H"
unsigned char Key_Read();

然后创建bsp_key.c文件,往里写入:

#include"bsp_key.h"
unsigned char Key_Read()
{
    unsigned int key_new;
    unsigned char key_value;
    P44=0;P42=1;P35=1;P34=1;
    key_new = P3 & 0X0F; 
    P44=1;P42=0;P35=1;P34=1;
	key_new = (key_new<<4)|(P3 & 0X0F);
    P44=1;P42=1;P35=0;P34=1;
	key_new = (key_new<<4)|(P3 & 0X0F);
    P44=1;P42=1;P35=1;P34=0;
	key_new = (key_new<<4)|(P3 & 0X0F);
    switch(~key_new)
    {
        case 0x8000: key_value=4;break;
        case 0x4000: key_value=5;break;
        case 0x2000: key_value=6;break;
		case 0x1000: key_value=7;break;
    
    	case 0x0800: key_value=8;break;
    	case 0x0400: key_value=9;break;
        case 0x0200: key_value=10;break;
    	case 0x0100: key_value=11;break;
            
        case 0x0080: key_value=12;break;
    	case 0x0040: key_value=13;break;
        case 0x0020: key_value=14;break;
    	case 0x0010: key_value=15;break;
            
        case 0x0008: key_value=12;break;
    	case 0x0004: key_value=13;break;
        case 0x0002: key_value=14;break;
    	case 0x0001: key_value=15;break;     
        default: key_value=0;break;
    }
    return key_value;
}

2.4 数码管

老套路创建bsp_seg.h,往里写入:

#include "STC15F2K60S2.H"
void Seg_Trans(unsigned char *seg_string,unsigned char *seg_buf);
void Seg_Disp(unsigned char*seg_buf,unsigned char pos);

然后创建bsp_seg.c,往里写入:

#include "bsp_seg.H"
void Seg_Trans(unsigned char *seg_string,unsigned char *seg_buf)
{
	unsigned char i,j;
	unsigned char temp;
	for(i=0,j=0;j<8;j++)
	{
		switch(seg_string[i])
		{
			case '0': 	temp=0xc0; 	break;
			case '1':	temp=0xf9;	break;
			case '2': 	temp=0xa4;	break;
			case '3':	temp=0xb0;	break;	
			case '4': 	temp=0x99;	break;
			case '5':	temp=0x92;	break;
			case '6':	temp=0x82;	break;
			case '7':	temp=0xf8;	break;
			case '8': 	temp=0x80;	break;
			case '9': 	temp=0x90;	break;
			case '-': 	temp=0xbf;	break;
			case ' ': 	temp=0xff;	break;
			default :	temp=0xff;	break;	
		}
		seg_buf[j]=temp;
		i++;
	}
}
void Seg_Disp(unsigned char*seg_buf,unsigned char pos)
{
	P0 = 0XFF;
	P2 = P2 & 0X1F | 0Xe0;
	P2 &= 0X1F;
	
	P0 = 0X01 << pos;
	P2 = P2 & 0X1F | 0Xc0;
	P2 &= 0X1F;
	
	P0 =seg_buf[pos];
	P2 = P2 & 0X1F | 0Xe0;
	P2 &= 0X1F;
}

2.5 定时器

定时器这个模板不用想,我们可以去那个烧写的软件里面找:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hJhzh171-1647304468424)(C:\Users\Wind\AppData\Roaming\Typora\typora-user-images\image-20220314181650106.png)]

老套路 创建timer.h

#include"STC15F2K60S2.H"
void Timer1Init(void);

创建timer.c

#include"timer.h"
void Timer1Init(void)		//1毫秒@12.000MHz
{
	AUXR |= 0x40;		//定时器时钟1T模式
	TMOD &= 0x0F;		//设置定时器模式
	TL1 = 0x20;		//设置定时初始值
	TH1 = 0xD1;		//设置定时初始值
	TF1 = 0;		//清除TF1标志
	TR1 = 1;		//定时器1开始计时
	
	ET1=1;
}

2.6 初始化

十分常见的一个模板,基本上每年赛题都会使用。创建一个bsp_init.h

#include"STC15F2K60S2.H"
void Cls_init();

创建bsp_init.c

#include " bsp_init.h"
void Cls_init()
{
    P0 = 0XFF;
    P2 = P2 & 0X1F | 0X80;
    P2 &= 0X1F;
    
    P0=0;
    P2 = P2 & 0X1F | 0XA0;
    P2 &= 0X1F;
}

总结:以上就是各个模板的写入,越熟练越好,做到闭着眼睛都能写出来的那种。接下来,我们就只需要去拼积木。

三、主函数的编写

说到主函数的编写,其实就是编写4个函数,一个中断服务函数,一个led显示函数(Led_Proc),一个数码管显示函数(Seg_Proc),一个按键扫描函数(Key_Proc),其中按键又是非常接近底层的,所以我们可以先从编写扫描按键函数来入手这个赛题。

我们创建main.c文件

#include "bsp_led.h"
#include "bsp_seg.h"
#include "bsp_iic.h"
#include "bsp_key.h"
#include "timer.h"
#include "bsp_init.h"
#include "stdio.h"// (sprintf用)              //一定不要落下某些函数的头文件,好像编译器不会报错,但会导致工作不正常
unsigned int Key_Slow_Down;
unsigned int Seg_Slow_Down;
unsigned long tick;//模仿32中的滴答定时器,可以定49天
unsigned long led_tick;//led流转专用
//++++++++++++++++++++按键专用++++++++++++++++++++++
unsigned char key_value,key_old,key_down;
//++++++++++++++++++++数码管显示专用+++++++++++++++++
unsigned char seg_string[10],seg_buf[8];
unsigned char pos;
//++++++++++++++++++++led显示用++++++++++++++++++++
unsigned char ucled;
unsigned char sz_mode=0;//设置模式的跳转,0-2,0什么都不显示,1设置led运行模式,2设置该led模式下的流转时间
unsigned int led_running[4]={400,400,400,400};//led下流转时间的数组
unsigned char EEPROM_led_running[4]={0,0,0,0};//用于写入EEPROM的数组
unsigned char led_mode=1;//led模式
bit led_flag=1;//该位可以控制led的亮灭
bit xssz_mode;//亮度显示设置位
bit buling;//数码管闪动
unsigned char Bright_Level;//显示亮度等级设置
unsigned char Bright_Level_Sister,Bright_Disp_Ctrl;//显示亮度等级设置
code unsigned char led_disp_matrix[4][8]={
	0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,//第一种模式,L1-L8
	0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, //第二种模式,L8-L1
	0x81, 0x42, 0x24, 0x18,	0x81, 0x42, 0x24, 0x18,//第三种模式,从两边到中间
	0x18, 0x24, 0x42, 0x81, 0x18, 0x24, 0x42, 0x81//第四种模式,从中间到两边
};
unsigned char j;
void Led_Proc();
void Seg_Proc();
void Key_Proc();
void main()
{
    Cls_Init();
    Timer1Init();
    EA=1;
    EEPROM_Read(EEPROM_led_running,16,4);//向EEPROM中读取数据
    if(EEPROM_led_running[1]>12||EEPROM_led_running[0]>12||EEPROM_led_running[2]>12||EEPROM_led_running[3]>12)
    {
        EEPROM_led_running[0]=6;
        EEPROM_led_running[2]=6;
        EEPROM_led_running[3]=6;
        EEPROM_led_running[1]=6;
	}//因为原始EEPROM有数据,所以只要里面存的数据大于1200就说明不是之前存的。
    led_running[0] = EEPROM_led_running[0] * 100;
	led_running[1] = EEPROM_led_running[1] * 100;
	led_running[2] = EEPROM_led_running[2] * 100;
	led_running[3] = EEPROM_led_running[3] * 100;
    while(1)
    {
     	Led_Proc();
        Key_Proc();
        Seg_Proc();
    }
}
void tm1_isr()interrupt 3
{
    if(++Key_Slow_Down==10) Key_Slow_Down=0;
    if(++Seg_Slow_Down==500) Seg_Slow_Down=0;   //减速
    tick++;
    if(tick%800==0)
    {
        buling =~ buling;
    }
    
   	switch(Bright_Level)
    {
        case 1:Bright_Level_Sister = 0; break;
        case 2:Bright_Level_Sister = 3; break;
        case 3:Bright_Level_Sister = 8; break;
        case 4:Bright_Level_Sister = 11; break;
    }
   	Bright_Disp_Ctrl++;
    if(Bright_Disp_Ctrl==13)
        Bright_Disp_Ctrl=0;
    if(Bright_Disp_Ctrl <= Bright_Level_Sister)//实现亮度大小切换
		Led_Disp(ucled);//LED显示
	else 
		Led_Disp(0);//led灭	
    
    Seg_Disp(seg_buf,pos);
    if(++pos==8) pos=0;
}
void Key_Proc()
{
    if(Key_Slow_Down) return ;
    Key_Slow_Down=1;
    	
    key_value = Key_Read();
    key_down = key_value & (key_value ^ key_old);//按键里面的精华,使得一直按着的话会无效
    key_old=key_value;
    
    switch(key_down)
    {
        case 4:
            sz_mode++;
            if(sz_mode==3)
            {
                sz_mode=0;
                EEPROM_led_running[0]=led_running[0]/100;
                EEPROM_led_running[1]=led_running[1]/100;
                EEPROM_led_running[2]=led_running[2]/100;
                EEPROM_led_running[3]=led_running[3]/100;
                EEPROM_Write(EEPROM_led_running,16,4);
			}
            break;
        case 5:
            led_flag =~ led_flag;
            break;
        case 9:
            switch(sz_mode)
            {
                case 1:
                    led_mode++;
                    if(led_mode==5)
                        led_mode=1;
                    break;
                case 2:
                    led_running[led_mode-1]+=100;
                    if(led_running[led_mode-1]==1300)
                        led_running[led_mode-1]=1200;
                    break;
            }
            break;
        case 8:
            switch(sz_mode)
            {
                case 1:
                    led_mode--;
                    if(led_mode==0)
                        led_mode=1;
                    break;
                case 2:
                    led_running[led_mode-1]-=100;
                    if(led_running[led_mode-1]==300)
                        led_running[led_mode-1]=400;
                    break;
            }
            break;
    }
    if((sz_mode==0)&&(key_old==8))
    {
        xssz_mode=1;
    }
    else 
        xssz_mode=0;
}
void Seg_Proc()
{
		if(Seg_Slow_Down) return;
		Seg_Slow_Down = 1;//减速程序
    switch(sz_mode)
    {
        case 1:
        case 2:
            sprintf(seg_string,"-%d- %4d",(unsigned int)led_mode,(unsigned int)led_running[led_mode-1]);
						break;
        case 0:
            sprintf(seg_string,"        ");
						break;
		}
    if(buling==1)
    {
        switch(sz_mode)
        {
            case 1:
                seg_string[1]=' ';
                break;
            case 2:
                seg_string[4]=' ';
                seg_string[5]=' ';
                seg_string[6]=' ';
                seg_string[7]=' ';
                break;
			}
		}
    Bright_Level = (Pcf8591_Adc(Adj_Res_Channel)/64+1);
    if(xssz_mode==1)
    {
         sprintf(seg_string,"      -%d",(unsigned int) Bright_Level);
    }
    Seg_Trans(seg_string,seg_buf);
}
void Led_Proc()
{
 	if(led_flag)
    {
        if(tick-led_tick>=led_running[led_mode-1])
        {
						led_tick=tick;
            ucled=led_disp_matrix[led_mode-1][j];
            j++;
            if(j==8)
            {
                if(++led_mode==5) led_mode=1;
                j=0;
            }
		}
    }
    else
        ucled=0;
}

总结:上述代码,只有main.c文件经过了测试,模板代码是我默写的,就没测试了,以上写代码思路以及模板还有这种“搭积木的思想”都是蚂蚁工厂里面的老师所传授的。

你可能感兴趣的:(单片机,单片机,蓝桥杯)