第十一屆藍橋杯單片機國賽

什么?4T模擬賽和省賽做起來輕輕松松?不妨來挑戰一下第十一屆國賽,這一屆的國賽居然沒考超聲波、串口通信!只要你正確地理解了題目的意思,規避出題人挖的坑,拿個國一輕輕松松。

附件:第十一屆藍橋杯單片機國賽
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

一、題目分析以及底層代碼搭建

1.內存空間不足

看完一遍題目,感覺挺正常的,不知道哪里有坑,其實,在開頭的硬件框圖中,出題人已經告訴了你本題最大的坑就是存儲空間不夠。在備賽專欄前篇中有提到,如果你定義變量的時候沒有加上存儲區修飾符時,該變量的存儲區修飾符默認是xdate型的,xdata 變量存儲在單片機的外部RAM中,訪問速度最慢,并且默認值通常不為0。
所以如果你沒有指定存儲區修飾符,會導致以下結果:

  • 對于需要頻繁使用的變量(比如定時器、Led、Seg相關變量),訪問速度一旦慢就會導致時序不精確、程序性能下降,一個程序使用很多需要頻繁訪問的變量時會導致程序運行到某一部分時程序崩潰,你如果做過跟串口通信有關的題目就對此深有體會了吧,串口數據緩存區會時不時的惡心你一下。
  • 編譯時提示錯誤,如code size exceeds available memory,燒錄時失敗,提示存儲空間不足。

解決方法:

  • 減少xdate的使用,多使用pdateidate
  • 對于程序中不會更改的常量前綴加上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){//}
}

如果程序有錯誤或者不理解的地方,請及時聯系我。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/72022.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/72022.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/72022.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

大彩串口屏開發 —— MODBUS通信

目 錄 Modbus通信方式 1 使用變量與協議設置方式 2 使用LUA腳本方式 3 兩者結合 Modbus通信 大彩串口屏可以采用三種方式實現與其它設備進行modbus通信和邏輯處理。 方式 1 使用變量與協議設置 步驟1 在協議設置里進行設置&#xff0c;包括開啟modbus協議&#xff0c;屏做為主…

【Linux docker】關于docker啟動出錯的解決方法。

無論遇到什么docker啟動不了的問題 就是 查看docker狀態sytemctl status docker查看docker日志sudo journalctl -u docker.service查看docker三個配置文件&#xff08;可能是配置的時候格式錯誤&#xff09;&#xff1a;/etc/docker/daemon.json&#xff08;如果存在&#xf…

怎么實現: 大語言模型微調案例

怎么實現: 大語言模型微調案例 目錄 怎么實現: 大語言模型微調案例輸入一個反常識的問題:首都在北京天安門之后對輸出模型進行測試:首都在北京天安門微調代碼:測試微調模型代碼:微調輸出模型結構輸出模型參數大小對比Qwen 2.5_0.5:53MB輸出模型:951MB 是一樣的,沒有進行…

rdiff-backup備份

目錄 1. 服務器備份知識點 1.1 備份策略 1.2 備份步驟和寶塔面板簡介 1.3 CentOS7重要目錄 2. 備份工具 2.1 tar -g 備份演示 2. rsync 備份演示 3. rdiff-backup 備份演示 4. 差異和優缺點 3. rdiff-backup安裝和使用 3.1 備份命令rdiff-backup 3.2 恢復命令--…

Claude:AI領域的多面手,從語言模型到智能編碼

文章目錄 引言Claude的起源與發展1. Claude的誕生2. Claude 3.7 Sonnet的突破 版本迭代技術原理Claude的獨特優勢混合推理模式成本與性能的平衡開發者友好的工具 功能及應用Claude的未來展望結論 引言 Claude是由Anthropic公司開發的大型語言模型&#xff0c;在人工智能領域&a…

RocketMQ 詳細教程(Spring Boot Spring Cloud Alibaba)

1. RocketMQ 簡介 RocketMQ 是阿里巴巴開源的一款分布式消息隊列&#xff0c;具有高吞吐量、低延遲、可靠性等特點&#xff0c;廣泛應用于金融、電商、物聯網等領域。 RocketMQ 的核心特性&#xff1a; 高可靠性&#xff1a;支持消息存儲、重復消費、失敗重試等高可用性&…

Spring(七)AOP-代理模式

目錄 代理模式 一 靜態代理 一、核心作用 二、使用場景 二 動態代理 一、核心作用 二、使用場景 具體實現&#xff1a;&#xff08;初始&#xff09; 具體實現&#xff1a;&#xff08;改進&#xff09; 一、核心業務邏輯 1. 接口 MathCalculator 2. 實現類 MathCa…

Java Lambda表達式:現代編程的簡潔之道

引言 在Java 8中&#xff0c;Lambda表達式的引入標志著Java語言向函數式編程邁出了重要一步。Lambda不僅簡化了代碼結構&#xff0c;還提升了開發效率&#xff0c;使得Java能夠更靈活地應對現代編程需求。本文將深入探討Lambda表達式的核心概念、語法規則、應用場景及其對Java…

BGP分解實驗·21——BGP選路原則之本地優先級

當使用BGP路徑屬性——本地優先級&#xff0c;進行路由優選時&#xff0c;優選“本地優先級”數值較大的那個。&#xff08;eBGP之間更新不攜帶這個屬性&#xff09; 實驗拓撲如下&#xff1a; 在未實現本地優先級策略前&#xff0c;先在各個BGP之間配置完成基本連接。 R1的基…

【redis】應用場景:共享會話和手機驗證碼

文章目錄 共享會話實現思路 手機驗證碼實現思路偽代碼實現生成驗證碼驗證驗證碼 共享會話 實現思路 如果每個應用服務器&#xff0c;維護自己的會話數據&#xff0c;此時彼此之間胡共享&#xff0c;用戶請求訪問到不同的服務器上&#xff0c;就可能會出現一些不能正確處理的情…

通義萬相 2.1 + 藍耘算力,AI 視頻生成的夢幻組合

在這個科技日新月異的時代&#xff0c;人工智能不斷刷新著我們對世界的認知。一次偶然的機會&#xff0c;我借助北京藍耘科技股份有限公司提供的算力支持&#xff0c;踏上了使用通義萬相 2.1 進行 AI 視頻生成的奇妙之旅。 目錄 1.1初遇藍耘科技&#xff1a; 1.2通義萬相 2.1…

【Go萬字洗髓經】Golang內存模型與內存分配管理

本文目錄 1. 操作系統中的虛擬內存分頁與進程管理虛擬內存與內存隔離 2. Golang中的內存模型內存分配流程內存單元mspan線程緩存mcache中心緩存mcentral全局堆緩存mheapheapArena空閑頁索引pageAlloc 3. Go對象分配mallocgc函數tiny對象分配內存 4.結合GMP模型來看內存模型tiny…

33.HarmonyOS NEXT NumberBox 步進器高級技巧與性能優化

HarmonyOS NEXT NumberBox 步進器高級技巧與性能優化 一、高級交互設計 1. 組件聯動控制 // 與Slider雙向綁定 State value: number 50Slider({value: this.value,onChange: (v) > this.value v })NumberBox({value: this.value,onChange: (v) > this.value v })2. …

關于ModbusTCP/RTU協議轉Ethernet/IP(CIP)協議的方案

IGT-DSER智能網關模塊支持西門子、倍福(BECKHOFF)、羅克韋爾AB&#xff0c;以及三菱、歐姆龍等各種品牌的PLC之間通訊&#xff0c;支持Ethernet/IP(CIP)、Profinet(S7)&#xff0c;以及FINS、MC等工業自動化常用協議&#xff0c;同時也支持PLC與Modbus協議的工業機器人、智能儀…

通義萬相2.1 × 藍耘智算:AIGC 界的「黃金搭檔」如何重塑創作未來?

在人工智能生成內容&#xff08;AIGC&#xff09;領域&#xff0c;通義萬相2.1與藍耘智算的結合&#xff0c;正以技術協同效應重新定義創作的可能性。這一組合不僅突破了傳統創作工具的效率瓶頸&#xff0c;更通過算法與算力的深度融合&#xff0c;為影視、廣告、游戲、教育等領…

【FreeRTOS】FreeRTOS操作系統在嵌入式單片機上裸機移植

目錄 一 RTOS概述 二 FreeRTOS移植 三 FreeRTOS使用 四 附錄 一 RTOS概述 先了解一些基礎概念&#xff0c;以下內容摘自FreeRTOS官網&#xff08;FreeRTOS? - FreeRTOS?&#xff09;&#xff1a; 【1】RTOS基礎知識 實時操作系統 (RTOS) 是一種體積小巧、確定性強的計算機…

文件包含漏洞第一關

一、什么是文件包含漏洞 1.文件包含漏洞概述 和SQL注入等攻擊方式一樣&#xff0c;文件包含漏洞也是一種注入型漏洞&#xff0c;其本質就是輸入一段用戶能夠控制的腳本或者代碼&#xff0c;并讓服務端執行。 什么叫包含呢&#xff1f;以PHP為例&#xff0c;我們常常把可重復使…

瑞芯微RK3576(1)-硬件設計

過年期間&#xff0c;趁著放假時間做了一款3576的核心板 方案是2G DDR432G emmc 引出所有IO口 關于接口方面&#xff0c;考慮了一段時間&#xff0c;最終決定使用BTB的模式&#xff0c;主要是能夠出更多的IO&#xff0c;方便拆卸&#xff0c;最讓我擔心的是BTB的位置問題 為了…

Java 大視界 -- Java 大數據在智能醫療藥品研發數據分析與決策支持中的應用(126)

&#x1f496;親愛的朋友們&#xff0c;熱烈歡迎來到 青云交的博客&#xff01;能與諸位在此相逢&#xff0c;我倍感榮幸。在這飛速更迭的時代&#xff0c;我們都渴望一方心靈凈土&#xff0c;而 我的博客 正是這樣溫暖的所在。這里為你呈上趣味與實用兼具的知識&#xff0c;也…

JWT的學習

1、HTTP無狀態及解決方案 HTTP一種是無狀態的協議&#xff0c;每次請求都是一次獨立的請求&#xff0c;一次交互之后就是陌生人。 以CSDN為例&#xff0c;先登錄一次&#xff0c;然后瀏覽器退出&#xff0c;這個時候在進入CSDN&#xff0c;按理說服務器是不知道你已經登陸了&…