第十一届蓝桥杯单片机国赛
什么?4T模拟赛和省赛做起来轻轻松松?不妨来挑战一下第十一届国赛,这一届的国赛居然没考超声波、串口通信!只要你正确地理解了题目的意思,规避出题人挖的坑,拿个国一轻轻松松。
附件:第十一届蓝桥杯单片机国赛
一、题目分析以及底层代码搭建
1.内存空间不足
看完一遍题目,感觉挺正常的,不知道哪里有坑,其实,在开头的硬件框图中,出题人已经告诉了你本题最大的坑就是存储空间不够。在备赛专栏前篇中有提到,如果你定义变量的时候没有加上存储区修饰符时,该变量的存储区修饰符默认是xdate
型的,xdata
变量存储在单片机的外部RAM中,访问速度最慢,并且默认值通常不为0。
所以如果你没有指定存储区修饰符,会导致以下结果:
- 对于需要频繁使用的变量(比如定时器、Led、Seg相关变量),访问速度一旦慢就会导致时序不精确、程序性能下降,一个程序使用很多需要频繁访问的变量时会导致程序运行到某一部分时程序崩溃,你如果做过跟串口通信有关的题目就对此深有体会了吧,串口数据缓存区会时不时的恶心你一下。
- 编译时提示错误,如
code size exceeds available memory
,烧录时失败,提示存储空间不足。
解决方法:
- 减少
xdate
的使用,多使用pdate
和idate
- 对于程序中不会更改的常量前缀加上
code
- 减少
float
型变量的使用
2.隐藏条件
其他要求的第四点指出:参数在参数设置时不生效,所以进行参数设置的时候,要再定义一个新的变量存放参数原来的值进行更改数值。
3.初始化底层代码
Init.h
#ifndef __Init_H__
#define __Init_H__
void SystemInit();
#endif
Init.c
#include <STC15F2K60S2.H>
void SystemInit()
{
P0 = 0xff;
P2 = P2 & 0x1f | 0x80;
P2 &= 0x1f;
P0 = 0x00;
P2 = P2 & 0x1f | 0xa0;
P2 &= 0x1f;
}
4.Led底层代码
Led.h
#ifndef __Led_H__
#define __Led_H__
void LedDisp(unsigned char *ucLed);
#endif
Led.c
#include <STC15F2K60S2.H>
void LedDisp(unsigned char *ucLed)
{
unsigned char i, temp = 0x00;
static unsigned char temp_old = 0xff;
for(i = 0; i < 8; i++)
temp |= (ucLed[i] << i);
if(temp_old != temp)
{
P0 = ~temp;
P2 = P2 & 0x1f | 0x80;
P2 &= 0x1f;
temp_old = temp;
}
}
5.Key底层代码
Key.h
#ifndef __Key_H__
#define __Key_H__
unsigned char KeyDisp();
#endif
Key.c
#include <STC15F2K60S2.H>
unsigned char KeyDisp()
{
unsigned char temp = 0;
P44 = 0;
P42 = 1;
P35 = 1;
P34 = 1;
if(P32 == 0) temp = 5;
if(P33 == 0) temp = 4;
P44 = 1;
P42 = 0;
P35 = 1;
P34 = 1;
if(P32 == 0) temp = 9;
if(P33 == 0) temp = 8;
return temp;
}
6.Seg底层代码
Seg.h
#ifndef __Seg_H__
#define __Seg_H__
void SegDisp(unsigned char wela, unsigned char dula, unsigned char point);
#endif
Seg.c
#include <STC15F2K60S2.H>
code unsigned char Seg_Table[] =
{
0xc0, //0
0xf9, //1
0xa4, //2
0xb0, //3
0x99, //4
0x92, //5
0x82, //6
0xf8, //7
0x80, //8
0x90, //9
0xff, //空
0xbf, //-
0x8e, //F
0x89, //H
0x9a //S
};
void SegDisp(unsigned char wela, unsigned char dula, unsigned char point)
{
P0 = 0xff;
P2 = P2 & 0x1f | 0xe0;
P2 &= 0x1f;
P0 = (0x01 << wela);
P2 = P2 & 0x1f | 0xc0;
P2 &= 0x1f;
P0 = Seg_Table[dula];
if(point)
P0 &= 0x7f;
P2 = P2 & 0x1f | 0xe0;
P2 &= 0x1f;
}
7.ds1302底层代码
ds1302.h
#ifndef __ds1302_H__
#define __ds1302_H__
void SetRtc(unsigned char *Rtc);
void GetRtc(unsigned char *Rtc);
#endif
ds1302.c
#include <STC15F2K60S2.H>
#include <intrins.h>
sbit SCK = P1^7;
sbit RST = P1^3;
sbit SDA = P2^3;
void Write_Ds1302(unsigned char temp)
{
unsigned char i;
for (i=0;i<8;i++)
{
SCK = 0;
SDA = temp&0x01;
temp>>=1;
SCK=1;
}
}
void Write_Ds1302_Byte( unsigned char address,unsigned char dat )
{
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(address);
Write_Ds1302(dat);
RST=0;
}
unsigned char Read_Ds1302_Byte ( unsigned char address )
{
unsigned char i,temp=0x00;
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(address);
for (i=0;i<8;i++)
{
SCK=0;
temp>>=1;
if(SDA)
temp|=0x80;
SCK=1;
}
RST=0; _nop_();
SCK=0; _nop_();
SCK=1; _nop_();
SDA=0; _nop_();
SDA=1; _nop_();
return (temp);
}
code unsigned char DS1302_Arr[4] = {0x84,0x82,0x80,0x8E};
void SetRtc(unsigned char *Rtc)
{
unsigned char i;
Write_Ds1302_Byte(DS1302_Arr[3],0x00);
for(i = 0; i < 3; i++)
Write_Ds1302_Byte(DS1302_Arr[i],Rtc[i]);
Write_Ds1302_Byte(DS1302_Arr[3],0x80);
}
void GetRtc(unsigned char *Rtc)
{
unsigned char i;
for(i = 0; i < 3; i++)
Rtc[i] = Read_Ds1302_Byte(DS1302_Arr[i]+1);
}
8.ds18b20底层代码
ds18b20.h
#ifndef __ds18b20_H__
#define __ds18b20_H__
float TemRead();
#endif
ds18b20.c
#include <STC15F2K60S2.H>
sbit DQ = P1^4;
//
void Delay_OneWire(unsigned int 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;
}
//
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 TemRead()
{
idata unsigned char TL, TH;
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0x44);
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);
TL = Read_DS18B20();
TH = Read_DS18B20();
return ((TH << 8) | TL) / 16.0;
}
9.iic底层代码
iic.h
#ifndef __iic_H__
#define __iic_H__
unsigned char AverageFilter();
#endif
iic.c
#include <STC15F2K60S2.H>
#include <intrins.h>
#define DELAY_TIME 5
sbit scl = P2^0;
sbit sda = P2^1;
static void I2C_Delay(unsigned char n)
{
do
{
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
}
while(n--);
}
void I2CStart(void)
{
sda = 1;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 0;
I2C_Delay(DELAY_TIME);
scl = 0;
}
//
void I2CStop(void)
{
sda = 0;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 1;
I2C_Delay(DELAY_TIME);
}
//
void I2CSendByte(unsigned char byt)
{
unsigned char i;
for(i=0; i<8; i++){
scl = 0;
I2C_Delay(DELAY_TIME);
if(byt & 0x80){
sda = 1;
}
else{
sda = 0;
}
I2C_Delay(DELAY_TIME);
scl = 1;
byt <<= 1;
I2C_Delay(DELAY_TIME);
}
scl = 0;
}
//
unsigned char I2CReceiveByte(void)
{
unsigned char da;
unsigned char i;
for(i=0;i<8;i++){
scl = 1;
I2C_Delay(DELAY_TIME);
da <<= 1;
if(sda)
da |= 0x01;
scl = 0;
I2C_Delay(DELAY_TIME);
}
return da;
}
//
unsigned char I2CWaitAck(void)
{
unsigned char ackbit;
scl = 1;
I2C_Delay(DELAY_TIME);
ackbit = sda;
scl = 0;
I2C_Delay(DELAY_TIME);
return ackbit;
}
//
void I2CSendAck(unsigned char ackbit)
{
scl = 0;
sda = ackbit;
I2C_Delay(DELAY_TIME);
scl = 1;
I2C_Delay(DELAY_TIME);
scl = 0;
sda = 1;
I2C_Delay(DELAY_TIME);
}
unsigned char Ad()
{
unsigned char temp;
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
I2CSendByte(0x01);
I2CWaitAck();
I2CStart();
I2CSendByte(0x91);
I2CWaitAck();
temp = I2CReceiveByte();
I2CSendAck(1);
I2CStop();
return temp;
}
unsigned char AverageFilter()
{
unsigned char i;
unsigned int sum = 0;
for (i = 0; i < 10; i++)
{
sum += Ad();// 多次采样
}
return (unsigned char)(sum / 10);
}
10.main.c
使用下面这个模板做省赛题是可以的,做国赛题就有点难受了,但是指定了存储区修饰符还是可以做的,如果你学过计算机操作系统或者嵌入式操作系统,可以试着用调度器来更改以下代码。
#include <STC15F2K60S2.H>
#include "Init.h"
#include "LED.h"
#include "Key.h"
#include "Seg.h"
#include "ds1302.h"
#include "ds18b20.h"
#include "iic.h"
/* 变量声明区 */
unsigned char Key_Slow; //按键减速变量 10ms
unsigned char Key_Val, Key_Down, Key_Up, Key_Old; //按键检测四件套
unsigned int Seg_Slow; //数码管减速变量 500ms
unsigned char Seg_Buf[] = {10,10,10,10,10,10,10,10,10,10};//数码管缓存数组
unsigned char Seg_Pos;//数码管缓存数组专用索引
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显示数据存放数组
unsigned int Time_1s, f;
/* 按键处理函数 */
void Key_Proc()
{
if(Key_Slow) return;
Key_Slow = 1; //按键减速
Key_Val = Key();
Key_Down = Key_Val & ~Key_Old;
Key_Up = ~Key_Val & Key_Old;
Key_Old = Key_Val;
}
/* 信息处理函数 */
void Seg_Proc()
{
if(Seg_Slow) return;
Seg_Slow = 1; //数码管减速
}
/* 其他显示函数 */
void Led_Proc()
{
}
/* 定时器0只用于计数 */
void Timer0_Init(void) //1毫秒@12.000MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x05;
TL0 = 0; //设置定时初始值
TH0 = 0; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
/* 定时器1用于计时 */
void Timer1_Init(void) //1毫秒@12.000MHz
{
AUXR &= 0xBF; //定时器时钟12T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x18; //设置定时初始值
TH1 = 0xFC; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1 = 1;
EA = 1;
}
/* 定时器1中断服务函数 */
void Timer1_Server() interrupt 3
{
if(++Key_Slow == 10) Key_Slow = 0; //按键延迟
if(++Seg_Slow == 100) Seg_Slow = 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]);
/* NE555 */
if(++Time_1s == 1000)
{
Time_1s = 0;
f = (TH0 << 8) | TL0;
TH0 = TL0 = 0;
}
}
void main()
{
Init();
Timer0_Init();
Timer1_Init();
while(1)
{
Key_Proc();
Seg_Proc();
Led_Proc();
}
}
二、数码管模块
1.代码框架构建
可以定义bit MainMode
来控制主页面(0-数据页面 1-参数页面),在数据页面中使用unsigned char SegMode
来控制显示页面(0-时间显示 1-温度显示 2-亮暗状态),在参数页面中使用unsigned char SetMode
来控制显示页面(0-时间参数 1-温度参数 2-指示灯参数)。
可以得到以下伪代码:
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long int u32;
/* 界面参数 */
idata bit MainMode; //主界面 0-数据界面 1-参数界面
idata u8 SegMode; //数据界面分界面 0-时间 1-温度 2-亮暗状态
idata u8 SetMode; //参数界面分界面 0-时间参数 1-温度参数 2-指示灯参数
void SegProc()
{
if(!MainMode)//数据界面
{
switch(SegMode)
{
case 0://时间显示页面
//...
break;
case 1://温度显示页面
//...
break;
case 2://亮暗状态显示页面
//...
break;
}
}
else//参数界面
{
switch(SetMode)
{
case 0://时间参数
//...
break;
case 1://温度参数
//...
break;
case 2://指示灯参数
//...
break;
}
}
}
void KeyProc()
{
KeyVal = KeyDisp();
KeyDown = KeyVal & ~KeyOld;
KeyDown = ~KeyVal & KeyOld;
KeyOld = KeyVal;
switch(KeyDown)
{
case 4:
MainMode = !MainMode;
break;
case 5:
if(!MainMode)//数据界面
{
SegMode++;
if(SegMode == 3)
SegMode = 0;
}
else//参数界面
{
SetMode++;
if(SetMode == 3)
SetMode = 0;
}
break;
}
}
2.数据页面
时间显示页面和温度显示页面都很容易实现,这边不使用float
型变量去接收实时温度读取函数的返回值,而是使用unsigned int
类型变量去接收实时温度读取函数的返回值放大10倍的值。
也可以在数码管函数内直接处理温度读取和时钟读取,但是可能数码管函数返回时有任务没做完,最好是分成多个处理函数分别处理对应的任务。
/* 温度 */
idata u16 Tem_10x; //温度读取放大10倍
/* 时间 */
pdata u8 Rtc[3] = {0x16,0x59,0x50};
void DS18B20Proc()
{
Tem_10x = TemRead() * 10;
}
void DS1302Proc()
{
GetRtc(Rtc);
}
void SegProc()
{
unsigned char i;
if(!MainMode)//数据界面
{
switch(SegMode)
{
case 0:
SegPoint[2] = 0;
SegBuf[2] = SegBuf[5] = 11;
for(i = 0; i < 3; i++)
{
SegBuf[3*i] = Rtc[i] / 16;
SegBuf[3*i+1] = Rtc[i] % 16;
}
break;
case 1:
SegBuf[0] = 12;
SegBuf[1] = SegBuf[2] = SegBuf[3] = SegBuf[4] = 10;
SegBuf[5] = Tem_10x / 100;
SegBuf[6] = Tem_10x / 10 % 10;
SegBuf[7] = Tem_10x % 10;
SegPoint[6] = 1;
break;
}
}
}
void main()
{
SystemInit();
Timer0_Init();
SetRtc(Rtc);//上电时设置当前时间
while(1)
{
//...
}
}
判断电压低于多少时为遮光状态,这个数值是需要你根据板子上的情况进行判断的,先假设一个数值为遮光临界值,然后用夹逼法慢慢求出最接近遮光状态的数值,一般来说,电压低于1V为遮光状态。
同样的,为了避免使用float
型变量,下面也是使用unsigned int
型变量接受光敏电阻的电压放大100倍后的电压值。
/* AD */
idata u16 RD1_100x; //光敏电阻电压放大100倍
idata bit DarkFlag; //环境为暗检测标志位 0-亮 1-暗
void ADProc()
{
RD1_100x = AverageFilter() / 51.0 * 100;
DarkFlag = (RD1_100x < 100) ? 1 : 0;
}
void SegProc()
{
unsigned char i;
if(!MainMode)//数据界面
{
switch(SegMode)
{
case 0:
//...
break;
case 1:
//...
break;
case 2:
SegBuf[0] = 13;
SegBuf[1] = 10;
SegBuf[2] = RD1_100x / 100;
SegBuf[3] = RD1_100x / 10 % 10;
SegBuf[4] = RD1_100x % 10;
SegBuf[5] = 10;
SegBuf[6] = 10;
SegBuf[7] = DarkFlag;
SegPoint[2] = 1;
SegPoint[6] = 0;
break;
}
}
}
3.参数页面
参数界面的实现是很容易的,这边注意的是参数在退出时才生效,所以每个参数都要再定义一个新的参数接收原始值。
/* 温度 */
idata u16 Tem_10x; //温度读取放大10倍
idata u8 TemSet = 25; //温度参数,默认值25,范围00~99
idata u8 TemSetDis = 25; //温度参数在修改过程中的值
/* 时间 */
pdata u8 Rtc[3] = {0x16,0x59,0x50};
idata u8 HourSet = 17; //小时参数,默认值17,范围00~23
idata u8 HourSetDis = 17; //小时参数在修改过程中的值
/* LED */
pdata u8 ucLed[8] = {0,0,0,0,0,0,0,0};
idata u8 LedSet = 4; //指示灯参数,默认值4,范围4~8
idata u8 LedSetDis = 4; //指示灯参数修改过程中的值
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long int u32;
/* 界面参数 */
idata bit MainMode; //主界面 0-数据界面 1-参数界面
idata u8 SegMode; //数据界面分界面 0-时间 1-温度 2-亮暗状态
idata u8 SetMode; //参数界面分界面 0-时间参数 1-温度参数 2-指示灯参数
/* AD */
idata u16 RD1_100x; //光敏电阻电压放大100倍
idata bit DarkFlag; //环境为暗检测标志位 0-亮 1-暗
/* 温度 */
idata u16 Tem_10x; //温度读取放大10倍
idata u8 TemSet = 25; //温度参数,默认值25,范围00~99
idata u8 TemSetDis = 25; //温度参数在修改过程中的值
/* 时间 */
pdata u8 Rtc[3] = {0x16,0x59,0x50};
idata u8 HourSet = 17; //小时参数,默认值17,范围00~23
idata u8 HourSetDis = 17; //小时参数在修改过程中的值
/* LED */
pdata u8 ucLed[8] = {0,0,0,0,0,0,0,0};
idata u8 LedSet = 4; //指示灯参数,默认值4,范围4~8
idata u8 LedSetDis = 4; //指示灯参数修改过程中的值
/* 数码管 */
idata u8 SegPos;
pdata u8 SegBuf[8] = {10,10,10,10,10,10,10,10};
pdata u8 SegPoint[8] = {0,0,0,0,0,0,0,0};
void DS18B20Proc()
{
Tem_10x = TemRead() * 10;
}
void DS1302Proc()
{
GetRtc(Rtc);
}
void ADProc()
{
RD1_100x = AverageFilter() / 51.0 * 100;
DarkFlag = (RD1_100x < 100) ? 1 : 0;
}
void SegProc()
{
unsigned char i;
if(!MainMode)//数据界面
{
//...
}
else//参数界面
{
switch(SetMode)
{
case 0:
SegPoint[2] = SegPoint[6] = 0;
SegBuf[0] = 13;
SegBuf[1] = 4;
SegBuf[2] = 10;
SegBuf[3] = 10;
SegBuf[4] = 10;
SegBuf[5] = 10;
SegBuf[6] = (HourSetDis / 10) ? HourSetDis / 10 : 10;
SegBuf[7] = HourSetDis % 10;
break;
case 1:
SegBuf[0] = 13;
SegBuf[1] = 5;
SegBuf[6] = (TemSetDis / 10) ? TemSetDis / 10 : 10;
SegBuf[7] = TemSetDis % 10;
break;
case 2:
SegBuf[0] = 13;
SegBuf[1] = 6;
SegBuf[6] = 10;
SegBuf[7] = LedSetDis;
break;
}
}
}
4.数码管完整代码
void SegProc()
{
unsigned char i;
if(!MainMode)//数据界面
{
switch(SegMode)
{
case 0:
SegPoint[2] = 0;
SegBuf[2] = SegBuf[5] = 11;
for(i = 0; i < 3; i++)
{
SegBuf[3*i] = Rtc[i] / 16;
SegBuf[3*i+1] = Rtc[i] % 16;
}
break;
case 1:
SegBuf[0] = 12;
SegBuf[1] = SegBuf[2] = SegBuf[3] = SegBuf[4] = 10;
SegBuf[5] = Tem_10x / 100;
SegBuf[6] = Tem_10x / 10 % 10;
SegBuf[7] = Tem_10x % 10;
SegPoint[6] = 1;
break;
case 2:
SegBuf[0] = 13;
SegBuf[1] = 10;
SegBuf[2] = RD1_100x / 100;
SegBuf[3] = RD1_100x / 10 % 10;
SegBuf[4] = RD1_100x % 10;
SegBuf[5] = 10;
SegBuf[6] = 10;
SegBuf[7] = DarkFlag;
SegPoint[2] = 1;
SegPoint[6] = 0;
break;
}
}
else//参数界面
{
switch(SetMode)
{
case 0:
SegPoint[2] = SegPoint[6] = 0;
SegBuf[0] = 13;
SegBuf[1] = 4;
SegBuf[2] = 10;
SegBuf[3] = 10;
SegBuf[4] = 10;
SegBuf[5] = 10;
SegBuf[6] = (HourSetDis / 10) ? HourSetDis / 10 : 10;
SegBuf[7] = HourSetDis % 10;
break;
case 1:
SegBuf[0] = 13;
SegBuf[1] = 5;
SegBuf[6] = (TemSetDis / 10) ? TemSetDis / 10 : 10;
SegBuf[7] = TemSetDis % 10;
break;
case 2:
SegBuf[0] = 13;
SegBuf[1] = 6;
SegBuf[6] = 10;
SegBuf[7] = LedSetDis;
break;
}
}
}
三、按键模块
S4和S5已经在数码管模块中说过了,这边需要注意的是切换主页面进入的是对应主页面的第一个页面。
S8和S9的实现也是很简单的,只需要注意每一个参数的初始值和边界值就可以了。
/* LED */
idata u8 LedSet = 4; //指示灯参数,默认值4,范围4~8
idata u8 LedSetDis = 4; //指示灯参数修改过程中的值
/* 温度 */
idata u16 Tem_10x; //温度读取放大10倍
idata u8 TemSet = 25; //温度参数,默认值25,范围00~99
idata u8 TemSetDis = 25; //温度参数在修改过程中的值
/* 时间 */
pdata u8 Rtc[3] = {0x16,0x59,0x50};
idata u8 HourSet = 17; //小时参数,默认值17,范围00~23
idata u8 HourSetDis = 17; //小时参数在修改过程中的值
/* 按键 */
idata u8 KeyVal,KeyDown,KeyUp,KeyOld;
void KeyProc()
{
KeyVal = KeyDisp();
KeyDown = KeyVal & ~KeyOld;
KeyDown = ~KeyVal & KeyOld;
KeyOld = KeyVal;
switch(KeyDown)
{
case 4:
if(!MainMode)
{
TemSetDis = TemSet;
HourSetDis = HourSet;
LedSetDis = LedSet;
MainMode = 1;
SetMode = 0;
}
else
{
TemSet = TemSetDis;
HourSet = HourSetDis;
LedSet = LedSetDis;
MainMode = 0;
SegMode = 0;
}
break;
case 5:
if(!MainMode)//数据界面
{
SegMode++;
if(SegMode == 3)
SegMode = 0;
}
else//参数界面
{
SetMode++;
if(SetMode == 3)
SetMode = 0;
}
break;
case 8://减按键
if(MainMode)
{
if(SetMode == 0)
{
if(--HourSetDis == 255)
HourSetDis = 0;
}
else if(SetMode == 1)
{
if(--TemSetDis == 255)
TemSetDis = 0;
}
else
{
if(--LedSetDis == 3)
LedSetDis = 4;
}
}
break;
case 9://加按键
if(MainMode)
{
if(SetMode == 0)
{
if(++HourSetDis == 24)
HourSetDis = 23;
}
else if(SetMode == 1)
{
if(++TemSetDis == 100)
TemSetDis = 99;
}
else
{
if(++LedSetDis == 9)
LedSetDis = 8;
}
}
break;
}
}
四、Led模块
/* LED */
pdata u8 ucLed[8] = {0,0,0,0,0,0,0,0};
idata u8 LedSet = 4; //指示灯参数,默认值4,范围4~8
idata u8 LedSetDis = 4; //指示灯参数修改过程中的值
idata bit L1Light; //指示灯L1点亮标志位 0灭 1亮
idata bit L2Light; //指示灯L2点亮标志位 0灭 1亮
idata bit L3Light; //指示灯L3点亮标志位 0灭 1亮
idata u16 Time_3s; //暗环境计时变量
idata u16 Time_3000ms; //亮环境计时变量
- L1使能判断:若判断当前时间处于小时参数整点至下一个 8 时之间,指示灯 L1 点亮,反之熄灭。
下一个8时,简单理解就是24+8=32时,所以只需要判断当前时间是否在小时参数和32区间即可,注意的是,当前时间是以十六进制存储的,所以比较时要转换成十进制再比较。
void DS1302Proc()
{
unsigned char Hour;
GetRtc(Rtc);
Hour = (Rtc[0] / 16) * 10 + Rtc[0] % 16;
L1Light = (HourSet <= Hour && Hour < 32);
}
void LedProc()
{
ucLed[0] = L1Light;
}
- L2使能判断:若判断当采集到的温度数据小于温度参数,指示灯 L2 点亮,反之熄灭。
void DS18B20Proc()
{
Tem_10x = TemRead() * 10;
L2Light = (Tem_10x < TemSet * 10);
}
void LedProc()
{
ucLed[1] = L2Light;
}
- L3使能判断:若判断环境处于“暗”状态,且持续时间超过 3 秒,指示灯 L3 点亮;环境处于“亮”状态,且持续时间超过 3 秒,指示灯 L3 熄灭
L3使能是有很多种方法的,当处于黑暗环境下时,对应的计时变量开始计时,超过3秒时让L3点亮,处于明亮环境下也一样。
什么时候清除计时变量呢?可以在黑暗环境下清除明亮环境的计时变量,在明亮环境清除黑暗环境下的计时变量。
/* LED */
pdata u8 ucLed[8] = {0,0,0,0,0,0,0,0};
idata u8 LedSet = 4; //指示灯参数,默认值4,范围4~8
idata u8 LedSetDis = 4; //指示灯参数修改过程中的值
idata bit L1Light; //指示灯L1点亮标志位 0灭 1亮
idata bit L2Light; //指示灯L2点亮标志位 0灭 1亮
idata bit L3Light; //指示灯L3点亮标志位 0灭 1亮
idata u16 Time_3s; //暗环境计时变量
idata u16 Time_3000ms; //亮环境计时变量
void ADProc()
{
RD1_100x = AverageFilter() / 51.0 * 100;
DarkFlag = (RD1_100x < 100) ? 1 : 0;
DarkFlag ? (Time_3000ms = 0) : (Time_3s = 0);
}
void LedProc()
{
ucLed[2] = L3Light;
}
void Timer0_Isr(void) interrupt 1
{
if(++SegPos == 8)
SegPos = 0;
SegDisp(SegPos,SegBuf[SegPos],SegPoint[SegPos]);
if(DarkFlag)//黑暗环境
{
if(++Time_3s >= 3001)
{
Time_3s = 3001;
L3Light = 1;
}
}
else//光明环境
{
if(++Time_3000ms >= 3001)
{
Time_3000ms = 3001;
L3Light = 0;
}
}
}
- L4~L8使能判断:若判断环境处于“暗”状态,通过 LED 指示灯参数指定的 LED 指示灯点亮,反之熄灭, L4-L8 中未被指定的 LED 指示灯应处于熄灭状态。
由于点亮/熄灭指示灯是可以自定义的,所以需要加上互斥点亮(如果你只写ucLed[LedSet-1]=DarkFlag
,不能实现这种情况:在黑暗环境下,第四个灯亮,然后进入参数页面改成第五个灯亮,这样子第四个、第五个灯都会亮。)
/* LED */
pdata u8 ucLed[8] = {0,0,0,0,0,0,0,0};
idata u8 LedSet = 4; //指示灯参数,默认值4,范围4~8
idata u8 LedSetDis = 4; //指示灯参数修改过程中的值
idata bit L1Light; //指示灯L1点亮标志位 0灭 1亮
idata bit L2Light; //指示灯L2点亮标志位 0灭 1亮
idata bit L3Light; //指示灯L3点亮标志位 0灭 1亮
idata u16 Time_3s; //暗环境计时变量
idata u16 Time_3000ms; //亮环境计时变量
void LedProc()
{
if(DarkFlag)
{
for(i = 3; i < 8; i++)
ucLed[i] = (i == LedSet-1);
}
else
for(i = 3; i < 8; i++)
ucLed[i] = 0;
}
五、代码整合
#include <STC15F2K60S2.H>
#include "Init.h"
#include "Seg.h"
#include "Key.h"
#include "Led.h"
#include "iic.h"
#include "ds1302.h"
#include "ds18b20.h"
typedef unsigned char u8;
typedef unsigned int u16;
/* 界面参数 */
idata bit MainMode; //主界面 0-数据界面 1-参数界面
idata u8 SegMode; //数据界面分界面 0-时间 1-温度 2-亮暗状态
idata u8 SetMode; //参数界面分界面 0-时间参数 1-温度参数 2-指示灯参数
/* AD */
idata u16 RD1_100x; //光敏电阻电压放大100倍
idata bit DarkFlag; //环境为暗检测标志位 0-亮 1-暗
/* 温度 */
idata u16 Tem_10x; //温度读取放大10倍
idata u8 TemSet = 25; //温度参数,默认值25,范围00~99
idata u8 TemSetDis = 25; //温度参数在修改过程中的值
/* 时间 */
pdata u8 Rtc[3] = {0x16,0x59,0x50};
idata u8 HourSet = 17; //小时参数,默认值17,范围00~23
idata u8 HourSetDis = 17; //小时参数在修改过程中的值
/* 按键 */
idata u8 KeyVal,KeyDown,KeyUp,KeyOld;
/* LED */
pdata u8 ucLed[8] = {0,0,0,0,0,0,0,0};
idata u8 LedSet = 4; //指示灯参数,默认值4,范围4~8
idata u8 LedSetDis = 4; //指示灯参数修改过程中的值
idata bit L1Light; //指示灯L1点亮标志位 0灭 1亮
idata bit L2Light; //指示灯L2点亮标志位 0灭 1亮
idata bit L3Light; //指示灯L3点亮标志位 0灭 1亮
idata u16 Time_3s; //暗环境计时变量
idata u16 Time_3000ms; //亮环境计时变量
/* 数码管 */
idata u8 SegPos;
pdata u8 SegBuf[8] = {10,10,10,10,10,10,10,10};
pdata u8 SegPoint[8] = {0,0,0,0,0,0,0,0};
void KeyProc()
{
KeyVal = KeyDisp();
KeyDown = KeyVal & ~KeyOld;
KeyDown = ~KeyVal & KeyOld;
KeyOld = KeyVal;
switch(KeyDown)
{
case 4:
if(!MainMode)
{
TemSetDis = TemSet;
HourSetDis = HourSet;
LedSetDis = LedSet;
MainMode = 1;
SetMode = 0;
}
else
{
TemSet = TemSetDis;
HourSet = HourSetDis;
LedSet = LedSetDis;
MainMode = 0;
SegMode = 0;
}
break;
case 5:
if(!MainMode)//数据界面
{
SegMode++;
if(SegMode == 3)
SegMode = 0;
}
else//参数界面
{
SetMode++;
if(SetMode == 3)
SetMode = 0;
}
break;
case 8://减按键
if(MainMode)
{
if(SetMode == 0)
{
if(--HourSetDis == 255)
HourSetDis = 0;
}
else if(SetMode == 1)
{
if(--TemSetDis == 255)
TemSetDis = 0;
}
else
{
if(--LedSetDis == 3)
LedSetDis = 4;
}
}
break;
case 9://加按键
if(MainMode)
{
if(SetMode == 0)
{
if(++HourSetDis == 24)
HourSetDis = 23;
}
else if(SetMode == 1)
{
if(++TemSetDis == 100)
TemSetDis = 99;
}
else
{
if(++LedSetDis == 9)
LedSetDis = 8;
}
}
break;
}
}
void SegProc()
{
unsigned char i;
if(!MainMode)//数据界面
{
switch(SegMode)
{
case 0:
SegPoint[2] = 0;
SegBuf[2] = SegBuf[5] = 11;
for(i = 0; i < 3; i++)
{
SegBuf[3*i] = Rtc[i] / 16;
SegBuf[3*i+1] = Rtc[i] % 16;
}
break;
case 1:
SegBuf[0] = 12;
SegBuf[1] = SegBuf[2] = SegBuf[3] = SegBuf[4] = 10;
SegBuf[5] = Tem_10x / 100;
SegBuf[6] = Tem_10x / 10 % 10;
SegBuf[7] = Tem_10x % 10;
SegPoint[6] = 1;
break;
case 2:
SegBuf[0] = 13;
SegBuf[1] = 10;
SegBuf[2] = RD1_100x / 100;
SegBuf[3] = RD1_100x / 10 % 10;
SegBuf[4] = RD1_100x % 10;
SegBuf[5] = 10;
SegBuf[6] = 10;
SegBuf[7] = DarkFlag;
SegPoint[2] = 1;
SegPoint[6] = 0;
break;
}
}
else//参数界面
{
switch(SetMode)
{
case 0:
SegPoint[2] = SegPoint[6] = 0;
SegBuf[0] = 13;
SegBuf[1] = 4;
SegBuf[2] = 10;
SegBuf[3] = 10;
SegBuf[4] = 10;
SegBuf[5] = 10;
SegBuf[6] = (HourSetDis / 10) ? HourSetDis / 10 : 10;
SegBuf[7] = HourSetDis % 10;
break;
case 1:
SegBuf[0] = 13;
SegBuf[1] = 5;
SegBuf[6] = (TemSetDis / 10) ? TemSetDis / 10 : 10;
SegBuf[7] = TemSetDis % 10;
break;
case 2:
SegBuf[0] = 13;
SegBuf[1] = 6;
SegBuf[6] = 10;
SegBuf[7] = LedSetDis;
break;
}
}
}
void LedProc()
{
unsigned char i;
ucLed[0] = L1Light;
ucLed[1] = L2Light;
ucLed[2] = L3Light;
if(DarkFlag)
{
for(i = 3; i < 8; i++)
ucLed[i] = (i == LedSet-1);
}
else
for(i = 3; i < 8; i++)
ucLed[i] = 0;
LedDisp(ucLed);
}
void DS18B20Proc()
{
Tem_10x = TemRead() * 10;
L2Light = (Tem_10x < TemSet * 10);
}
void DS1302Proc()
{
unsigned char Hour;
GetRtc(Rtc);
Hour = (Rtc[0] / 16) * 10 + Rtc[0] % 16;
L1Light = (HourSet <= Hour && Hour < 32);
}
void ADProc()
{
RD1_100x = AverageFilter() / 51.0 * 100;
DarkFlag = (RD1_100x < 100) ? 1 : 0;
DarkFlag ? (Time_3000ms = 0) : (Time_3s = 0);
}
void Timer0_Init(void) //1毫秒@12.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x18; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
EA = 1;
}
void Timer0_Isr(void) interrupt 1
{
if(++SegPos == 8)
SegPos = 0;
SegDisp(SegPos,SegBuf[SegPos],SegPoint[SegPos]);
if(DarkFlag)
{
if(++Time_3s >= 3001)
{
Time_3s = 3001;
L3Light = 1;
}
}
else
{
if(++Time_3000ms >= 3001)
{
Time_3000ms = 3001;
L3Light = 0;
}
}
}
void main()
{
SystemInit();
Timer0_Init();
SetRtc(Rtc);
while(1)
{
//
}
}
如果程序有错误或者不理解的地方,请及时联系我。