相关说明:
开发板:CT107D (IAP15F2K61S2)
开发环境:Keil5
涉及题目:第十届蓝桥杯单片机国赛真题
主要函数代码及说明:
#include "stdio.h"
#include "Timer.h"
#include "bsp_init.h"
#include "bsp_key.h"
#include "bsp_seg.h"
#include "bsp_led.h"
#include "bsp_onewire.h"
#include "bsp_iic.h"
#include "Usart.h"
#include "bsp_ultrasonic.h"
//--定时器滴答变量
unsigned long ms_Tick = 0;
//--长短按判别计时
xdata unsigned long Long_Short_Touch_ms_Tick = 0;
//--定时器减速变量
unsigned int Key_Slow_Down;
unsigned int Seg_Slow_Down;
unsigned int Led_Slow_Down;
unsigned int Volatge_Output_Slow_Down;
//--按键专用变量
unsigned char Key_Value;
unsigned char Key_Old, Key_Down;
//--长按按键功能函数是否执行过标志
bit S8_Long_Touch_Func_Exc_Flag = 0;
bit S9_Long_Touch_Func_Exc_Flag = 0;
//--数码管专用变量
unsigned char seg_string[10];
unsigned char seg_buf[8] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
unsigned char pos;
//--LED专用变量
unsigned char ucLed = 0x00;
bit Data_Compare_Interface_Flag = 0;//数据和参数界面标志位,0-数据界面,1-参数界面
unsigned char Data_Interface_Trans_Flag = 0;//数据界面三种显示标志,0-温度,1-距离,2-次数
bit Compare_Interface_Trans_Flag = 0;//参数界面两种显示标志,0-温度参数,1-距离参数
unsigned char Temp_Comp = 30;//温度参数比较值
unsigned char Dist_Comp = 35;//超声波距离参数比较值
unsigned char Temp_Comp_Old = 30;//温度参数进入时候的数值
unsigned char Dist_Comp_Old = 35;//超声波距离参数进入时候的数值
unsigned int Compare_Value_Change_Times = 1000;//参数变化次数
bit DA_Out_Ctrl_Flag = 0;//控制DA是否输出的标志0-启动DA输出,1-停止DA输出
xdata float Temp_Value;//温度传感器采集的数值
unsigned char Dist_Value;//超声波采集的距离值
//--EEPROM测试专用变量
unsigned char eeprom_string[2] = {0,0};//用于存储变化次数
//--串口专用变量
xdata unsigned int Usart_Slow_Down;
unsigned char Uart_Buf[15];//数据发送和接收的缓存区
unsigned char Uart_Buf_Index = 0;
unsigned char Uart_Send_Data_String[12];
void Key_Proc(void);
void Seg_Proc(void);
void Led_Proc(void);
void Data_Collect_Proc(void);//数据采集函数
void Delay750ms();//用来消除85°C显示
void main(void)
{
Cls_Peripheral();//关闭LED/继电器/蜂鸣器
Timer1Init();//定时器1初始化
EA = 1;//打开总中断
EEPROM_Read(eeprom_string, 0, 2);
Compare_Value_Change_Times = (eeprom_string[0]<<8)|(eeprom_string[1]);
UsartInit();
Ultrasonic_Timer0Init();
rd_temperature();
Delay750ms();//用于消除上电显示85°C的影响
while(1)
{
Key_Proc();
Data_Collect_Proc();
Seg_Proc();
Led_Proc();
Uart_Proc();
}
}
void Key_Proc(void)
{
if(Key_Slow_Down) return;
Key_Slow_Down = 1;
//三行代码
Key_Value = Key_Read();
Key_Down = Key_Value & (Key_Old ^ Key_Value);//下降沿,按得哪个按键,这个值就是几
Key_Old = Key_Value;//检测电平,一直按着一直等于一个数
if(Key_Down != 0)//表示有按键按下去,也就是按键产生了下降沿,开始计时
{
Long_Short_Touch_ms_Tick = ms_Tick;
S8_Long_Touch_Func_Exc_Flag = 0;//使能长按功能
S9_Long_Touch_Func_Exc_Flag = 0;//使能长按功能
}
if((ms_Tick - Long_Short_Touch_ms_Tick) < 1000)//如果数值小于一秒,且产生了上升沿,认为是短按
{
switch(Key_Up)//这个结构很常见,判别是哪个按键按下去了,根据按键按下的情况,控制内部数据变量的变化
{
case 5://界面按键 ,0-数据界面,1-参数界面
Data_Compare_Interface_Flag ^= 1;//完成数据界面和参数界面的切换
Data_Interface_Trans_Flag = 0;//切换后,保证默认为第一个界面
Compare_Interface_Trans_Flag = 0;//切换后,保证默认为第一个界面
if(Data_Compare_Interface_Flag == 1)//如果刚刚跳转到参数界面,存储当前的参数值
{
Temp_Comp_Old = Temp_Comp;
Dist_Comp_Old = Dist_Comp;
}
else //如果刚刚跳转到数据界面,判断数值是否发生变化,如果发生变化,变更次数+1
{
if((Temp_Comp_Old != Temp_Comp)||(Dist_Comp_Old != Dist_Comp))
Compare_Value_Change_Times++;
eeprom_string[0] = (Compare_Value_Change_Times>>8);//把高8位存储
eeprom_string[1] = (Compare_Value_Change_Times & 0x00ff);//把低8位存储
//--EEPROM测试
EEPROM_Write(eeprom_string, 0, 2);
}
break;//这个break很重要
case 4://切换按键
if(Data_Compare_Interface_Flag == 0)//在数据界面
{
if(++Data_Interface_Trans_Flag == 3) Data_Interface_Trans_Flag =0;//完成0-2的变化,0-温度,1-距离,2-次数
}
else //表示在参数界面
{
Compare_Interface_Trans_Flag ^= 1;//完成0-1切换,0-温度参数,1-距离参数
}
break;//这个break很重要
case 9://加功能
if((Data_Compare_Interface_Flag == 1)&&(Compare_Interface_Trans_Flag == 0))//如果是温度参数界面
Temp_Comp += 2;
else if((Data_Compare_Interface_Flag == 1)&&(Compare_Interface_Trans_Flag == 1))//如果是距离参数界面
Dist_Comp += 5;
if(Temp_Comp >= 99) Temp_Comp = 99;//压制到99
if(Dist_Comp >= 99) Dist_Comp = 99;
break;//这个break很重要
case 8://减功能
if((Data_Compare_Interface_Flag == 1)&&(Compare_Interface_Trans_Flag == 0))//如果是温度参数界面
Temp_Comp -= 2;
else if((Data_Compare_Interface_Flag == 1)&&(Compare_Interface_Trans_Flag == 1))//如果是距离参数界面
Dist_Comp -= 5;
if(Temp_Comp >= 200) Temp_Comp = 0;//压制到0
if(Dist_Comp >= 200) Dist_Comp = 0;
break;//这个break很重要
}
}
else //如果刚刚下降沿产生的时间,一旦超过1s,只要不更新Long_Short_Touch_ms_Tick数值,永远进入这个函数,认为是长按
{
if((Key_Old == 8)&&(S8_Long_Touch_Func_Exc_Flag == 0))//表示S8长按,且第一次执行
{
S8_Long_Touch_Func_Exc_Flag = 1;//立马变化,防止长按按钮没有释放的时候,再次执行
Compare_Value_Change_Times = 0;//变化次数变为0
eeprom_string[0] = 0;
eeprom_string[1] = 0;
EEPROM_Write(eeprom_string, 0, 2);
}
else if((Key_Old == 9)&&(S9_Long_Touch_Func_Exc_Flag == 0))//表示S9长按,且第一次执行
{
S9_Long_Touch_Func_Exc_Flag = 1;//立马变化,防止长按按钮没有释放的时候,再次执行
DA_Out_Ctrl_Flag ^= 1;//DA输出标志位翻滚
}
}
}
void Seg_Proc(void)
{
if(Seg_Slow_Down) return;
Seg_Slow_Down = 1;
if(Data_Compare_Interface_Flag == 0)//0-数据界面
{
switch(Data_Interface_Trans_Flag)//0-温度,1-距离,2-次数
{
case 0:
sprintf(seg_string,"C %5.2f",Temp_Value);
break;
case 1:
sprintf(seg_string,"L %02d",(unsigned int)Dist_Value);
break;
case 2:
sprintf(seg_string,"N %5d",(unsigned int)Compare_Value_Change_Times);
break;
}
}
else//1-参数界面
{
if(Compare_Interface_Trans_Flag == 0) //0-温度参数,1-距离参数
sprintf(seg_string,"P 1 %02d",(unsigned int)Temp_Comp);
else
sprintf(seg_string,"P 2 %02d",(unsigned int)Dist_Comp);
}
Seg_Tran(seg_string, seg_buf);
}
void Data_Collect_Proc(void)//每500ms采集一次数据
{
if(Data_Collect_Slow_Down) return;
Data_Collect_Slow_Down = 1;
Temp_Value = (rd_temperature()/16.0);//温度传感器采集的数值
Dist_Value = Wave_Recv();
if(Dist_Value > 99)
Dist_Value = 99;
//借用一片地方,用于DA的输出
if(DA_Out_Ctrl_Flag == 0)//0-启动 1-停止
{
if(Dist_Value <= Dist_Comp)
Pcf8591_Dac(102);//输出2v
else
Pcf8591_Dac(204);//输出4v
}
else
Pcf8591_Dac(20);//输出0.4v
}
void Uart_Proc(void)
{
if(Usart_Slow_Down) return;
Usart_Slow_Down = 1;
if((Uart_Buf[Uart_Buf_Index-1] == 0x0a)&&(Uart_Buf[Uart_Buf_Index-2] == 0x0d))//拿到的数据帧的倒数后两位为帧尾,表示收到一个数据帧
{
if((Uart_Buf_Index == 4)&&(Uart_Buf[0]=='S')&&(Uart_Buf[1]=='T'))//收到第一种指令ST
{
sprintf(Uart_Send_Data_String,"$%02d,%5.2f\r\n",(unsigned int)Dist_Value,Temp_Value);
}
else if ((Uart_Buf_Index == 6)&&(Uart_Buf[0]=='P')&&(Uart_Buf[1]=='A')&&(Uart_Buf[2]=='R')&&(Uart_Buf[3]=='A'))//收到第二种指令PARA
{
sprintf(Uart_Send_Data_String,"#%02d,%02d\r\n",(unsigned int)Dist_Comp,(unsigned int)Temp_Comp);
}
else//其他情况
{
sprintf(Uart_Send_Data_String,"ERROR\r\n");
}
Usart_Send_String(Uart_Send_Data_String);//自动寻找最后一个位置为\0地方,控制发送结束
Uart_Buf_Index = 0;
}
else if(Uart_Buf_Index > 6)//表示一直没有收到想要的数据帧
{
sprintf(Uart_Send_Data_String,"ERROR\r\n");
Usart_Send_String(Uart_Send_Data_String);//自动寻找最后一个位置为\0地方,控制发送结束
Uart_Buf_Index = 0;
}
}
void Led_Proc(void)
{
if(Led_Slow_Down) return;
Led_Slow_Down = 1;
//温度值比较
if(Temp_Value <= Temp_Comp)//熄灭L1
ucLed &= (~0x01);
else //点亮L1
ucLed |= 0x01;
//距离比较
if(Dist_Value < Dist_Comp)//点亮L2
ucLed |= 0x02;
else //熄灭L2
ucLed &= (~0x02);
//DA输出
if(DA_Out_Ctrl_Flag == 0)//启动状态,L3点亮
ucLed |= 0x04;
else //停止状态,L3熄灭
ucLed &= (~0x04);
}
void tm1_isr() interrupt 3
{
ms_Tick++;//滴答定时器,能记录的数据长达29天
if(++Key_Slow_Down == 10) Key_Slow_Down = 0;//减速变量区,控制子函数的刷新频率
if(++Seg_Slow_Down == 500) Seg_Slow_Down = 0;
if(++Led_Slow_Down == 100) Led_Slow_Down = 0;
if(++Volatge_Output_Slow_Down == 500) Volatge_Output_Slow_Down = 0;
if(++Usart_Slow_Down == 100) Usart_Slow_Down = 0;
Seg_Disp(seg_buf, pos);//用于数码管显示
if(++pos == 8) pos = 0;
Led_Disp(ucLed);//用于LED显示
}
void Uart() interrupt 4
{
if (RI)
{
RI = 0; //清除RI位
Uart_Buf[Uart_Buf_Index] = SBUF; //P0显示串口数据
Uart_Buf_Index++;//第n个数据过来之后,变成了n
}
}
void Delay750ms() //@12.000MHz 用来消除85°C显示
{
unsigned char i, j, k;
_nop_();
_nop_();
i = 35;
j = 51;
k = 182;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
省赛和国赛的题目最起码要做过一遍。主要训练逻辑思路,可以先用自己写好的各个模块驱动代码。比赛前几天全部代码都要重新写,包括新建工程开始,到各个驱动模块代码。比赛的时候,我是先写好大模板和各个模块的代码,然后才开始做按要求去实现功能的,功能由简单到难。