什么?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 5sbit 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);}elsefor(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);}elsefor(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){//}
}
如果程序有錯誤或者不理解的地方,請及時聯系我。