本次模擬賽涉及的模塊:基礎三件套(Led&Relay,按鍵、數碼管)+ 進階單件套(pcf8591的AD模塊)
附件:
各模塊底層代碼在文章的結尾
一、數碼管部分
1.頁面1
頁面1要顯示的格式是:
最左邊的數碼管顯示字母C
,最后兩位顯示濕度的十位和個位(不足兩位十位熄滅)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
C | 3 | 0 |
濕度是由pcf8591的AD模塊讀取通道3的電壓值轉換來的,他們的關系是(從圖轉換成函數)
h u m i d i t y = { 10 , U ≤ 1 0.266 ? U ? 16.666 , 1 ≤ U ≤ 4 90 , U ≥ 4 humidity = \begin{cases} 10,U\le1\\ 0.266*U-16.666,\,\,1\le U \le 4\\ 90,U\ge 4\\ \end{cases} humidity=? ? ??10,U≤10.266?U?16.666,1≤U≤490,U≥4?
所以在AD的處理函數就可以先將通道3的電壓讀出來后直接進行濕度轉換。下面給出的是用float型變量接收未放大的電壓值。
pcf8591.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 ADRead()
{unsigned char temp = 0;I2CStart();I2CSendByte(0x90);I2CWaitAck();I2CSendByte(0x03);I2CWaitAck();I2CStart();I2CSendByte(0x91);I2CWaitAck();temp = I2CReceiveByte();I2CSendAck(1);I2CStop();return temp;
}
main.c
中調用
/*濕度*/
idata u8 humidity;//電壓轉換濕度void ADProc()
{float RB2 = ADRead() / 51.0;//讀取RB2的電壓//濕度轉換if(RB2 <= 1.0)humidity = 10;else if(RB2 >= 4.0)humidity = 90;elsehumidity = 80/3.0*RB2+10-80/3.0;
}
void SegProc()
由于數碼管緩存區初始化時設置為8個數碼管均熄滅,所以只需對索引為0,6,7的數碼管賦值即可。
idata u8 SegPos;
pdata u8 SegBuf[8] = {10,10,10,10,10,10,10,10};void SegProc()
{switch(SegMode){case 0:SegBuf[0] = 11;//CSegBuf[6] = humidity / 10 ? humidity / 10 : 10;SegBuf[7] = humidity % 10;break;}
}void Timer0_Isr(void) interrupt 1
{if(++SegPos == 8) SegPos = 0;SegDisp(SegPos, SegBuf[SegPos]);
}
2.頁面2
頁面2要顯示的格式是:
最左邊的數碼管顯示字母E
,最后兩位顯示濕度參數的十位和個位(不足兩位十位熄滅)
濕度參數默認值為50
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
E | 5 | 0 |
這個就特別簡單了,頁面1的代碼直接復制下來就可以了,唯一要改的地方就是頁面2的顯示要對濕度參數的十位進行判斷,為0時熄滅。
idata u8 humidity_set = 50;void SegProc()
{switch(SegMode){case 1:SegBuf[0] = 12;//ESegBuf[6] = humidity_set / 10 ? humidity_set / 10 : 10;SegBuf[7] = humidity_set % 10;break;}
}
3. 頁面3
頁面3要顯示的格式是:
最左邊的數碼管顯示字母H
,最后兩位顯示時間間隔的十位和個位(不足兩位十位熄滅)
時間間隔默認值為3。
時間間隔是繼電器點亮的間隔,跟數碼管沒關系,直接顯示即可。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
E | 3 |
idata u8 time = 3;//時間間隔void SegProc()
{switch(SegMode){case 2:SegBuf[0] = 13;//HSegBuf[6] = time / 10 ? time / 10 : 10;SegBuf[7] = time % 10;break;}
}
二、按鍵部分
按鍵只用到了按鍵S4、S5、S8、S9。
按鍵S4是切換頁面,實現也很簡單。
按鍵5是繼電器啟動標志位,在任何界面按一次標志位取反。
idata bit relay_work; //繼電器工作 0-停止 1-開始void KeyProc()
{KeyVal = KeyDisp();KeyDown = KeyVal & ~KeyOld;KeyUp = ~KeyVal & KeyOld;KeyOld = KeyVal;switch(KeyDown){case 4://頁面流轉if(++SegMode == 3)SegMode = 0;break;case 5://切換繼電器工作模式relay_work = !relay_work;break;case 8://參數-if(SegMode == 1){humidity_set -= 5;if(humidity_set == 25)humidity_set = 90;}else if(SegMode == 2){if(--time == 0)time = 10;}break;case 9://參數+if(SegMode == 1){humidity_set += 5;if(humidity_set == 95)humidity_set = 30;}else if(SegMode == 2){if(++time == 11)time = 1;}break;}
}
三、繼電器部分
第一次寫繼電器的時候理解錯了,題目也沒有明確提到,根據第一次提交的測評分析這道題目的意思是繼電器工作模式下觸發吸合條件后繼電器吸合,經過一定時間間隔后斷開,持續處于該狀態不重復吸合,直到經過一次濕度>濕度參數后再次觸發條件(濕度<濕度參數),繼電器才吸合。
idata bit relay_work; //繼電器工作 0-停止 1-開始
idata bit relay_flag; //繼電器使能標志位
idata u16 TimeCount; //定時器計時變量
idata bit humidity_flag; //0-濕度值不小于參數
idata bit humidity_has_flag;//0-繼電器未重復吸合 1-繼電器已重復吸合過
void LedProc()
{/*Relay*/if(!relay_work)//繼電器不工作{relay_flag = 0;//關閉繼電器humidity_has_flag = 0;//繼電器重復吸合標志位清零}else//繼電器工作{humidity_flag = (humidity < humidity_set);//如果濕度小于濕度參數,humidity_flag為真if(!humidity_flag)//如果濕度大于濕度參數humidity_has_flag = 0;//重復吸合標志位清零if(humidity_flag && !humidity_has_flag)//如果濕度小于濕度參數并且還未觸發繼電器吸合{humidity_has_flag = 1;//繼電器已經吸合過了relay_flag = 1;//繼電器開始工作}}Relay(relay_flag);
}void Timer0_Isr(void) interrupt 1
{//如果處于繼電器工作模式下并且繼電器開始工作時計時if(relay_work && relay_flag){if(++TimeCount == time*1000)//吸合后達到一定時間間隔斷開繼電器{TimeCount = 0;//計時變量清零relay_flag = 0;//繼電器不工作}}
}
四、代碼整合(改一下main函數可以運行)
Init.c
#include <STC15F2K60S2.H>void SystemInit()
{P0 = 0xff;P2 = P2 & 0x1f | 0x80;P2 &= 0x1f;P0 = 0x00;P2 = P2 & 0x1f | 0xa0;P2 &= 0x1f;
}
Led.h
#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 != temp_old){P0 = ~temp;P2 = P2 & 0x1f | 0x80;P2 &= 0x1f;temp_old = temp;}
}void Relay(bit flag)
{unsigned char temp = 0x00;static unsigned char temp_old = 0xff;if(flag)temp |= 0x10;elsetemp &= 0xef;if(temp != temp_old){P0 = temp;P2 = P2 & 0x1f | 0xa0;P2 &= 0x1f;temp_old = temp;}
}
Key.h
#include <STC15F2K60S2.H>unsigned char KeyDisp()
{unsigned char temp = 0x00;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;
}
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, //空
0xc6, //C
0x86, //E
0x89 //H
};void SegDisp(unsigned char wela, unsigned char dula)
{P0 = 0xff;P2 = P2 & 0x1f | 0xe0;P2 &= 0x1f;P0 = (0x01 << wela);P2 = P2 & 0x1f | 0xc0;P2 &= 0x1f;P0 = Seg_Table[dula];P2 = P2 & 0x1f | 0xe0;P2 &= 0x1f;
}
pcf8591.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 ADRead()
{unsigned char temp = 0;I2CStart();I2CSendByte(0x90);I2CWaitAck();I2CSendByte(0x03);I2CWaitAck();I2CStart();I2CSendByte(0x91);I2CWaitAck();temp = I2CReceiveByte();I2CSendAck(1);I2CStop();return temp;
}
main.c
#include <STC15F2K60S2.H>
#include "Init.h"
#include "Led.h"
#include "Key.h"
#include "Seg.h"
#include "pcf8591.h"typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long int u32;/*按鍵*/
idata u8 KeyVal, KeyDown, KeyUp, KeyOld;
/*數碼管*/
idata u8 SegPos;
pdata u8 SegBuf[8] = {10,10,10,10,10,10,10,10};
idata u8 SegMode;
/*指示燈*/
pdata u8 ucLed[8] = {0,0,0,0,0,0,0,0};
idata bit relay_work; //繼電器工作 0-停止 1-開始
idata bit relay_flag; //繼電器使能標志位
idata u16 TimeCount; //定時器計時變量
/*濕度*/
idata u8 humidity;
idata u8 humidity_set = 50;
idata u8 time = 3;
idata bit humidity_flag; //0-濕度值不小于參數
idata bit humidity_has_flag; void KeyProc()
{KeyVal = KeyDisp();KeyDown = KeyVal & ~KeyOld;KeyUp = ~KeyVal & KeyOld;KeyOld = KeyVal;switch(KeyDown){case 4:if(++SegMode == 3)SegMode = 0;break;case 5:relay_work = !relay_work;break;case 8:if(SegMode == 1){humidity_set -= 5;if(humidity_set == 25)humidity_set = 90;}else if(SegMode == 2){if(--time == 0)time = 10;}break;case 9:if(SegMode == 1){humidity_set += 5;if(humidity_set == 95)humidity_set = 30;}else if(SegMode == 2){if(++time == 11)time = 1;}break;}
}void SegProc()
{switch(SegMode){case 0:SegBuf[0] = 11;//CSegBuf[6] = humidity / 10 ? humidity / 10 : 10;SegBuf[7] = humidity % 10;break;case 1:SegBuf[0] = 12;//ESegBuf[6] = humidity_set / 10 ? humidity_set / 10 : 10;SegBuf[7] = humidity_set % 10;break;case 2:SegBuf[0] = 13;//HSegBuf[6] = time / 10 ? time / 10 : 10;SegBuf[7] = time % 10;break;}
}void LedProc()
{u8 i;/*Relay*/if(!relay_work){relay_flag = 0;humidity_has_flag = 0;}else{humidity_flag = (humidity < humidity_set);if(!humidity_flag)humidity_has_flag = 0;if(humidity_flag && !humidity_has_flag){humidity_has_flag = 1;relay_flag = 1;}}Relay(relay_flag);/*Led*/for(i = 0; i < 3; i++)ucLed[i] = (i == SegMode);ucLed[3] = relay_work;LedDisp(ucLed);
}void ADProc()
{float RB2 = ADRead() / 51.0;if(RB2 <= 1.0)humidity = 10;else if(RB2 >= 4.0)humidity = 90;elsehumidity = 80/3.0*RB2+10-80/3.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]);if(relay_work && relay_flag){if(++TimeCount == time*1000){TimeCount = 0;relay_flag = 0;}}
}void main()
{SystemInit();Timer0_Init();while(1){//...}
}