此程序是基于51hei單片機開發板上面做的,如需要移植到自己的電路上,修改相應的端口即可,開發板完整的電路圖下載: 點這里 (注意:只需要看相關部分即可如 溫感 數碼管 蜂鳴器的使用的端口的連線,其他部分可以忽略)
/**
***********************************************************************************************************
* @file : main.c
* @author : 徐冉
* @date : 2014年4月27日08:40:23 ~ 2014年5月1日23:03:58
* @version : V1.2.3
* @brief : 基于DS18B20的帶記憶功能溫度報警系統 單片機STC89C52RC MCU 晶振 : 11.0592MHZ
* @note : 溫度報警值可由開發板鍵盤和紅外遙控器按鍵進行調整,調整后下次上電將保存上一次的調整值
* ------------ 溫度報警系統有三種狀態:當溫度達到設定的高溫預警值范圍時LED點陣將顯示"火",同時蜂鳴器
* ------------ 以8000HZ的頻率發聲報警,且LED小燈全亮;當溫度達到設定的低溫預警值時LED點陣屏將顯示“水”,
* ------------ 蜂鳴器將以4000HZ的頻率報警,且LED小燈全亮;當溫度處于正常溫度值時,LED點陣屏顯示“心形”,
* ------------ 蜂鳴器處于關閉狀態,且LED小燈全部熄滅。
***********************************************************************************************************
*/
#include <reg52.h>
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
sbit BUZZ = P1^6;
unsigned char code LedTable[] = {
0xC0, //"0"
0xF9, //"1"
0xA4, //"2"
0xB0, //"3"
0x99, //"4"
0x92, //"5"
0x82, //"6"
0xF8, //"7"
0x80, //"8"
0x90, //"9"
0x9C, // '小0'
0xC6, //'C'
0xBF //"-"
};
//數碼管顯示緩沖區+LED獨立小燈
unsigned char idata LedBuff[] = {0xC6, 0x9C, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F};
unsigned char pdata LedCode[3][8] = {
{0xFF,0x99,0x00,0x00,0x00,0x81,0xC3,0xE7}, //heart
{0xF7,0xD5,0xD5,0xE6,0xF7,0xEB,0xDD,0xBE}, //fire
{0xF7,0x77,0xA0,0xC3,0xE5,0xD6,0xB7,0x71} //water
};
//溫度狀態
unsigned char idata tempSta = 0;//0表示正常溫度,1表示高溫,2表示低溫
bit flag2s = 0;
unsigned char setTempIndex = 0;//0-正常運行狀態 1-8報警溫度設定索引
unsigned char thr0, tlr0;//T0定時器重載值
unsigned int counter = 0;//計數器
unsigned char idata thr1, tlr1;//00H-70H內存不夠,使用70H-FFH內存
bit buzzflag = 0;//蜂鳴器啟動標志
signed int shangxianHigh = 300, shangxianLow = 280;//溫度上限值高溫度值和低溫度值
signed int xiaxianHigh = 200, xiaxianLow = 180; //溫度下限值的高溫度值和低溫度值
signed char num[8] = {0, 0, 0, 0, 0, 0, 0, 0};//保存報警溫度值十位數和個位數
extern bit flagIrd; //紅外解碼完成標志
extern unsigned char IrdCode[4];//裝載紅外解碼值
/**************************local function definition***********************************/
void ConfigTimer0(unsigned int xms);
void TempToLedBuf(signed int temp);
void TempertureWarning(signed int temp);
void ConfigBuzzFr(unsigned int fr);
void ReadE2PROMToNumArrary();
extern void KeyDrive();
extern void KeyScan();
extern void LCD1602RefreshCoursor();
extern void InitLCD1602();
extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char * str);
extern bit StartConvertTemp();
extern bit ReadDS18B20Temperture(signed int * temperture);
extern void WriteEEPROMByte(unsigned char addr, unsigned char dat);
extern unsigned char ReadEEPROMByte(unsigned char addr);
extern void LEDRefreshPause();
extern void ContinueRefreshLED();
extern void ConfigInfrared();
extern void IrdKeyDrive();
void main(void)
{
ADDR3 = 1;
ENLED = 0;//選擇LED
InitLCD1602();
ConfigTimer0(1);//T0定時1ms
ConfigInfrared();
//ConfigBuzzFr(1000);//默認設定蜂鳴器頻率為1000
StartConvertTemp();//啟動一次溫度轉換
LcdShowStr(0, 0, "high T: XX-XX 'C");
LcdShowStr(0, 1, "low T: XX-XX 'C");//LCD初始化顯示
ReadE2PROMToNumArrary();
EA = 1; //打開總中斷
while (1)
{
KeyDrive(); //檢測按鍵動作
{
flagIrd = 0;
IrdKeyDrive();//紅外按鍵檢測
}
{
flag2s = 0;
if (ReadDS18B20Temperture(&temp))
{
TempToLedBuf(temp); //將溫度值轉換成數碼管數字并檢測溫度是否超限,超限啟動聲光報警!
}
}
}
}
void ReadE2PROMToNumArrary()
{
unsigned char str[4];
//數碼管初始化顯示0.00
LedBuff[2] = LedTable[0];
LedBuff[3] = LedTable[0];
LedBuff[4] = LedTable[0];
LedBuff[3] &= 0x7F;//add point
/*讀取eeprom中的數據*/
num[0] = ReadEEPROMByte(0x00);
num[1] = ReadEEPROMByte(0x01);
num[2] = ReadEEPROMByte(0x02);
num[3] = ReadEEPROMByte(0x03);
num[4] = ReadEEPROMByte(0x04);
num[5] = ReadEEPROMByte(0x05);
num[6] = ReadEEPROMByte(0x06);
num[7] = ReadEEPROMByte(0x07);
LedBuff[6] = 0x7F;//讀取成功指示
/*初始化更新設定的報警溫度值*/
shangxianLow = (num[0] * 100 + num[1] * 10);
shangxianHigh = (num[2] * 100 + num[3] * 10);
xiaxianLow = (num[4] * 100 + num[5] * 10);
xiaxianHigh = (num[6] * 100 + num[7] * 10);
/*將報警溫度值顯示到LCD1602上*/
str[0] = num[0] + '0';
str[1] = num[1] + '0';
str[2] = '\0';
LcdShowStr(8, 0, str);
str[0] = num[2] + '0';
str[1] = num[3] + '0';
str[2] = '\0';
LcdShowStr(11, 0, str);
str[0] = num[4] + '0';
str[1] = num[5] + '0';
str[2] = '\0';
LcdShowStr(8, 1, str);
str[0] = num[6] + '0';
str[1] = num[7] + '0';
str[3] = '\0';
LcdShowStr(11, 1, str);
}
void TempToLedBuf(signed int temp)
{
unsigned char buf[6];//緩沖區
signed char i = 0; //i必須是有符號型數據
{
buf[5] = LedTable[12];//添加負號
//保留一位小數
temp = (~temp + 1) * 0.0625 * 10;//負值,讀取到的16位二進制數值取反+1再*分辨率0.0625
for (i = 0; i < 4; i++) //溫度值最高3位數值+小數位
{
buf[i] = temp % 10;
temp /= 10;
}
//去掉無效位數字
for (i = 3; i > 1; i--)
{
if (buf[i] == 0)
{
buf[i] = 0xFF;//去掉高位0不顯示
}
else
{
break;//遇到第一個有效數字就退出
}
}
//轉換為實際的有效數字
for (; i >= 0; i--)
{
buf[i] = LedTable[buf[i]];
}
//將實際數字拷貝到數碼管緩沖區中
for (i = 0; i < 4; i++)
{
LedBuff[i+2] = buf[i];
}
}
else
{
//溫度值大于等于0,保留一位小數位
temp = (temp * 0.0625 * 10);//直接用讀取到的16位二進制數值*分辨率即是實際的溫度值
TempertureWarning(temp); //溫度報警檢測必須函數必須放在前面
for (i = 0; i < 4; i++)
{
buf[i] = temp % 10;
temp /= 10;
}
//去掉高位的0
for (i = 3; i > 1; i--)
{
if (buf[i] == 0)
{
buf[i] = 0xFF;
}
else
{
break;//遇到第一個有效數字就退出
}
}
//轉換為實際的有效數字
for (; i >= 0; i--)
{
buf[i] = LedTable[buf[i]];
}
//拷貝到緩沖區
for (i = 0; i < 4; i++)
{
LedBuff[i+2] = buf[i]; //最低兩位顯示溫度標示符
}
}
//在相應的位置點上小數點
LedBuff[3] &= 0x7F;//0 111 1111
}
void RefreshLCDDisplay()
{
unsigned char str[3];//字符串緩沖區
{
case 1: str[0] = num[0] + '0'; str[1] = '\0'; LcdShowStr(8, 0, str); break;
case 2: str[0] = num[1] + '0'; str[1] = '\0'; LcdShowStr(9, 0, str); break;//shangxianLow的十位和個位數字顯示刷新
case 3: str[0] = num[2] + '0'; str[1] = '\0'; LcdShowStr(11, 0, str); break;
case 4: str[0] = num[3] + '0'; str[1] = '\0'; LcdShowStr(12, 0, str); break;
case 5: str[0] = num[4] + '0'; str[1] = '\0'; LcdShowStr(8, 1, str); break;
case 6: str[0] = num[5] + '0'; str[1] = '\0'; LcdShowStr(9, 1, str); break;
case 7: str[0] = num[6] + '0'; str[1] = '\0'; LcdShowStr(11, 1, str); break;
case 8: str[0] = num[7] + '0'; str[1] = '\0'; LcdShowStr(12, 1, str); break;
default: break;
}
LCD1602RefreshCoursor();
}
void SetTempByKeyNum(unsigned char keyNum)
{
switch (setTempIndex)
{
case 1: num[0] = keyNum; break;
case 2: num[1] = keyNum; break;
case 3: num[2] = keyNum; break;
case 4: num[3] = keyNum; break;
case 5: num[4] = keyNum; break;
case 6: num[5] = keyNum; break;
case 7: num[6] = keyNum; break;
case 8: num[7] = keyNum; break;
default: break;
}
RefreshLCDDisplay();
setTempIndex++;//光標右移
if (setTempIndex > 8)
setTempIndex = 1;//設置光標回返
LCD1602RefreshCoursor();
}
void IncCoursorNum()
{
switch (setTempIndex)
{
case 1: {if (num[0] < 9) num[0]++; else num[0] = 0; break;} //十位數字++后重新設置溫度值
case 2: {if (num[1] < 9) num[1]++; else num[1] = 0; break;}//shangxianLow是小數*10之后的值
case 3: {if (num[2] < 9) num[2]++; else num[2] = 0; break;}
case 4: {if (num[3] < 9) num[3]++; else num[3] = 0; break;}
case 5: {if (num[4] < 9) num[4]++; else num[4] = 0; break;}
case 6: {if (num[5] < 9) num[5]++; else num[5] = 0; break;}
case 7: {if (num[6] < 9) num[6]++; else num[6] = 0; break;}
case 8: {if (num[7] < 9) num[7]++; else num[7] = 0; break;}
default: break;
}
LCD1602RefreshCoursor();//再次刷新光標顯示
}
void DecCoursorNum()
{
switch (setTempIndex) //根據光標閃爍的索引來遞減相應位置的數字
{
case 1: {if (num[0] > 0) --num[0]; else num[0] = 9; break;} //十位數字++后重新設置溫度值
case 2: {if (num[1] > 0) --num[1]; else num[1] = 9; break;}//shangxianLow是小數*10之后的值
case 3: {if (num[2] > 0) --num[2]; else num[2] = 9; break;}
case 4: {if (num[3] > 0) --num[3]; else num[3] = 9; break;}
case 5: {if (num[4] > 0) --num[4]; else num[4] = 9; break;}
case 6: {if (num[5] > 0) --num[5]; else num[5] = 9; break;}
case 7: {if (num[6] > 0) --num[6]; else num[6] = 9; break;}
case 8: {if (num[7] > 0) --num[7]; else num[7] = 9; break;}
default: break;
}
LCD1602RefreshCoursor();//刷新光標閃爍
}
void SetWarningTemperture()
{
/*設置溫度報警值*/
shangxianLow = (num[0] * 100 + num[1] * 10);
shangxianHigh = (num[2] * 100 + num[3] * 10);
xiaxianLow = (num[4] * 100 + num[5] * 10);
xiaxianHigh = (num[6] * 100 + num[7] * 10);
/*將數據保存到eeprom中*/
WriteEEPROMByte(0x00, num[0]);
WriteEEPROMByte(0x01, num[1]);
WriteEEPROMByte(0x02, num[2]);
WriteEEPROMByte(0x03, num[3]);
WriteEEPROMByte(0x04, num[4]);
WriteEEPROMByte(0x05, num[5]);
WriteEEPROMByte(0x06, num[6]);
WriteEEPROMByte(0x07, num[7]);
LedBuff[6] = 0xFE;//寫入成功指示
}
void TempertureWarning(signed int temp)
{
if ((temp > xiaxianLow) && (temp < xiaxianHigh)) //溫度在18.0-20.0度進行低溫報警
{
buzzflag = 1;//啟動報警器
ConfigBuzzFr(4000); //蜂鳴器發聲頻率2000HZ
LedBuff[6] = 0x00;//低溫亮8個LED小燈
tempSta = 2;//water
}
else if ((temp > shangxianLow) && (temp < shangxianHigh)) //溫度在28.0-30.0度進行高溫報警
{
buzzflag = 1;
ConfigBuzzFr(8000);//蜂鳴器發聲頻率5000HZ
LedBuff[6] = 0x00;//高溫亮八個LED小燈
tempSta = 1;//fire
}
else
{
buzzflag = 0;//關閉報警器
LedBuff[6] = 0xFF;//關閉報警燈
tempSta = 0;//heart
}
}
void ConfigTimer0(unsigned int xms)
{
unsigned long tmp;
tmp = 11059200/12;//周期頻率
tmp = (tmp * xms) / 1000;//定時xms需要的計數值
tmp = 65536 - tmp;//定時xms的定時器裝入初值
thr0 = (unsigned char)(tmp >> 8);
tlr0 = (unsigned char)tmp;
TMOD &= 0xF0;//清零T0控制位
TMOD |= 0x01;//T0方式1,16位定時器可以設定
TH0 = thr0;
TL0 = tlr0;
TR0 = 1;//開啟T0定時器
ET0 = 1;//開定時器T0中斷
}
void RefreshLEDChar()
{
static unsigned char index = 0;
P0 = 0xFF;//消隱
ADDR3 = 1;
if ((ENLED == 0) && (ADDR3 == 1))
{
P1 &= 0xF8;//清零P1口低三位
P1 |= index;//index控制三八譯碼器選擇地址
P0 = LedBuff[index++];
if (index > 6) //刷新LEDS0-LEDS6,6個數碼管+LED小燈
{
index = 0;
}
}
}
void RefreshLed()
{
static unsigned char index = 0;
P0 = 0xFF;
ADDR3 = 0;
if ((ENLED == 0) && (ADDR3 == 0))
{
P1 &= 0xF8;//清零P1口低三位
P1 |= index;
P0 = LedCode[tempSta][index++];//tempSta == 0表示溫度正常
if (index > 7)
index = 0;
//index &= 0x07;//到8歸0
}
}
void ConfigBuzzFr(unsigned int fr)
{
unsigned long tmp = 0;
tmp = 11059200/12; //周期頻率
tmp = tmp / fr; //設定fr頻率需要的計數值
tmp = 65536 - tmp;//設定fr頻率需要裝入的計數初值
thr1 = (unsigned char)(tmp >> 8);
tlr1 = (unsigned char)tmp; //計數值高低字節
TMOD &= 0x0F; //清零T1控制位
TMOD |= 0x10; //配置T1工作方式1,16位定時器模式
TH1 = thr1;
TL1 = tlr1;
TR1 = 1;//開啟T1
ET1 = 1;//開啟定時器T1中斷
}
void Timer0_ISP() interrupt 1
{
static bit biv = 0;
TH0 = thr0;
TL0 = tlr0;//重新裝入初值
counter++;
KeyScan(); //掃描按鍵
RefreshLEDChar(); //1ms數碼管顯示刷新
if (!biv) //2ms
{
RefreshLed();//點陣刷新
}
biv = ~biv; //分頻
if (counter >= 2000)
{
counter = 0;//2s
flag2s = 1;
}
}
void Timer1_ISP() interrupt 3
{
TH1 = thr1;
TL1 = tlr1;
{
BUZZ = ~BUZZ;//以frHZ驅動蜂鳴器發聲
}
}
/**
***********************************************************************************************
* @file : lcd1602.c
* @author : xr
* @date : 2014年4月27日08:40:23
* @version : V1.2.3
* @brief : LCD1602驅動
***********************************************************************************************
*/
#include <reg52.h>
sbit LCD1602_RS = P1^0;
sbit LCD1602_RW = P1^1;
sbit LCD1602_EN = P1^5;
//74HC138
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
bit tmpADDR1 = 0;//地址選擇緩沖區
void ContinueRefreshLED();
void LEDRefreshPause();
void LCD1602Wait()
{
unsigned char sta;
P0 = 0xFF;//讀狀態前先拉高P0口
/*讀狀態*/
LCD1602_RS = 0;
LCD1602_RW = 1;
LCD1602_EN = 0;
do
{
LCD1602_EN = 1;
sta = P0;
LCD1602_EN = 0;//關閉液晶輸出使能,避免液晶輸出數據影響總線上的其他器件
} while (sta & 0x80); //狀態字的最高位如果為1則表示液晶忙碌,禁止操作
}
void LEDRefreshPause()
{
ENLED = 1;//關閉LED使能
tmpADDR0 = ADDR0;//因為LED和LCD同時使用了P1^0和P1^1引腳,所以要暫時保存ADDR0和ADDR1的數據即LED掃描地址值
tmpADDR1 = ADDR1;
P0 = 0xFF; //數碼管+LED小燈去抖動
}
void ContinueRefreshLED()
{
ADDR0 = tmpADDR0;
ADDR1 = tmpADDR1;//恢復原來LED掃描的地址選擇值
ENLED = 0;//選擇LED
P0 = 0xFF; //數碼管和LED去抖
}
void LCD1602WriteCmd(unsigned char cmd)
{
LEDRefreshPause();//暫停LED掃描
//讀寫之前都要進行液晶的忙碌等待
LCD1602Wait();
LCD1602_RS = 0;
LCD1602_RW = 0;
LCD1602_EN = 0;
P0 = cmd;//送入命令
LCD1602_EN = 1;//高脈沖
LCD1602_EN = 0;//恢復原電平狀態,關閉液晶輸出
ContinueRefreshLED();//繼續掃描LED(數碼管+獨立LED小燈)
}
void LCD1602WriteData(unsigned char dat)
{
LEDRefreshPause();//暫停LED掃描
LCD1602Wait();
LCD1602_RS = 1;
LCD1602_RW = 0;
LCD1602_EN = 0;
P0 = dat;
LCD1602_EN = 1;//高脈沖
LCD1602_EN = 0;
ContinueRefreshLED();//繼續掃描LED
}
void InitLCD1602()
{
LCD1602WriteCmd(0x38);
LCD1602WriteCmd(0x0C);
LCD1602WriteCmd(0x06);
LCD1602WriteCmd(0x01);
}
void LCD1602SetCoursor(unsigned char x, unsigned char y)
{
unsigned char addr;
if (y == 0) //第一行
{
addr = 0x00 + x;//x為地址偏移
}
else
{
addr = 0x40 + x;
}
LEDRefreshPause();
LCD1602WriteCmd(addr + 0x80);//寫光標地址
ContinueRefreshLED();
}
void OpenCoursor()
{
LCD1602WriteCmd(0x0F);//顯示光標,且光標閃爍
}
void CloseCoursor()
{
LCD1602WriteCmd(0x0C);//關閉光標
}
void LcdShowStr(unsigned char x, unsigned char y, unsigned char * str)
{
unsigned char addr;
if (y == 0)
{
addr = 0x00 + x;
}
else
{
addr = 0x40 + x;
}
LEDRefreshPause();
LCD1602WriteCmd(addr + 0x80);
ContinueRefreshLED();
while (*str != '\0')
{
LEDRefreshPause();
LCD1602WriteData(*str++);
ContinueRefreshLED();
}
}
/**
***********************************************************************************************
* @file : ds18b20.c
* @author : xr
* @date : 2014年4月27日08:40:23
* @version : V1.2.3
* @brief : DS18B20驅動
***********************************************************************************************
*/
#include <reg52.h>
#include <intrins.h>
sbit DS18B20_IO = P3^2;//1ware總線引腳
void DelayX10us(unsigned char x10us)
{
do
{
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_(); //大約延時10us
} while (x10us--);
}
bit ReadDS18B20Ack()
{
bit ack = 0;
EA = 0;//先關閉所有中斷,以避免中斷時間影響DS18B20操作
DS18B20_IO = 0;//拉低
DelayX10us(70); //延時700us
DS18B20_IO = 1;
DelayX10us(6); //延時60us一定可以讀取到DS18B20的應答
ack = DS18B20_IO;//讀取存在脈沖
while (!DS18B20_IO);//等待DS18B20復位結束
EA = 1;//打開中斷
return (ack);
}
void WriteDS18B20Byte(unsigned char byte)
{
unsigned char mask = 0x01;//最低位開始寫
EA = 0;//關閉中斷
for (mask = 0x01; mask != 0; mask <<= 1)
{
DS18B20_IO = 0;//拉低
_nop_();
_nop_();//延時2us
if ((byte & mask) != 0)
{
DS18B20_IO = 1;//發送1
}
else
{
DS18B20_IO = 0;//發送0
} //拉低延時到把數據送入總線上的時間不得超過15us,15us后DS18B20進行對總線進行采樣
DelayX10us(6);//延時60us,等待DS18B20采樣完成
DS18B20_IO = 1;//釋放總線
}
EA = 1;//打開中斷
}
unsigned char ReadDS18B20Byte()
{
unsigned char mask = 0x01;//接收數據探測掩碼
unsigned char byte = 0;
EA = 0;//關閉中斷
for (mask = 0x01; mask != 0; mask <<= 1) //低位在先,逐位接收
{
DS18B20_IO = 0;
_nop_();
_nop_();//先拉低總線2us
DS18B20_IO = 1;
_nop_();
_nop_();//再拉高總線2us,然后主機進行對DS18B20_IO總線采樣
if (DS18B20_IO != 0)
{
byte |= mask;//相應位置1
}
else
{
byte &= (~mask);//相應位清零
}//主機在將DS18B20拉低再拉高,然后主機進行采樣,時間不超過15us
DelayX10us(6); //延時60us,這時總線將被上拉電阻拉高
}
EA = 1;//開中斷
return (byte); //返回接收到的數據
}
bit StartConvertTemp()
{
bit ack;
ack = ReadDS18B20Ack();//復位總線,并讀取DS18B20存在脈沖
if (ack == 0) //讀取到復位脈沖
{
WriteDS18B20Byte(0xCC);//跳過ROM尋址
WriteDS18B20Byte(0x44);//啟動溫度轉換
}
return (~ack);//ack==0表示溫度啟動轉換成功
}
bit ReadDS18B20Temperture(signed int * temperture)
{
unsigned char lsb, msb;
bit ack = 0;
ack = ReadDS18B20Ack();//復位總線,并讀取DS18B20存在脈沖
if (ack == 0)
{
WriteDS18B20Byte(0xCC);//跳過ROM
WriteDS18B20Byte(0xBE);//讀暫存數據寄存器
lsb = ReadDS18B20Byte();//讀溫度值低字節數據
msb = ReadDS18B20Byte();//讀溫度值高字節數據
/*將溫度值的高低字節合并成int型數據并存入*temperture中*/
*temperture = ((unsigned int)(msb) << 8) + lsb;
}
return (~ack);//ack==0則表示溫度值讀取成功
}
/**
***********************************************************************************************
* @file : main.c
* @author : xr
* @date : 2014年4月27日20:26:26
* @version : V1.2.3
* @brief : 按鍵驅動程序
***********************************************************************************************
*/
#include <reg52.h>
sbit KEY_IN_1 = P2^4;
sbit KEY_IN_2 = P2^5;
sbit KEY_IN_3 = P2^6;
sbit KEY_IN_4 = P2^7;
sbit KEY_OUT_1 = P2^3;
sbit KEY_OUT_2 = P2^2;
sbit KEY_OUT_3 = P2^1;
sbit KEY_OUT_4 = P2^0;
unsigned char keySta[4][4] = {1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1};
//按鍵到PC標準鍵盤編碼
unsigned char keyCodeMap[4][4] = {
'1', '2', '3', 0x26, //數字鍵123,向上鍵
'4', '5', '6', 0x25, //數字鍵456,向左鍵
'7', '8', '9', 0x28, //數字鍵789,向下鍵
'0', 0x1B, 0x0D, 0x27 //數字鍵0,向右鍵
};
unsigned char code IrdKeyCode[21][2] = {
{0x45, 0x00}, {0x46, 0x00}, {0x47, 0x00}, //前者為紅外遙控器鍵碼,后者為對應的標準PC鍵盤編碼,0x00表示無對應值
{0x44, 0x00}, {0x40, 0x25}, {0x43, 0x27}, //后退->向左鍵,前進->向右鍵
{0x07, 0x1B}, {0x15, 0x28}, {0x09, 0x26}, //EQ->ESC,-->向下鍵,+->向上鍵
{0x16, '0'}, {0x19, 0x00}, {0x0D, 0x0D}, //0->0, U/SD->回車鍵
{0x0C, '1'}, {0x18, '2'}, {0x5E, '3'}, //1->1, 2->2, 3->3
{0x08, '4'}, {0x1C, '5'}, {0x5A, '6'}, //4->4, 5->5, 6->6
{0x42, '7'}, {0x52, '8'}, {0x4A, '9'} //7->7, 8->8, 9->9
};
void KeyAction(unsigned char keycode);
/***************************extern function and values definition************************************/
extern unsigned char setTempIndex;
extern bit flagIrd; //紅外解碼完成標志
extern unsigned char IrdCode[4];//裝載紅外解碼值
extern void LCD1602SetCoursor(unsigned char x, unsigned char y);
extern void OpenCoursor();
extern void CloseCoursor();
extern void ContinueRefreshLED();
extern void LEDRefreshPause();
extern void IncCoursorNum();
extern void DecCoursorNum();
extern void SetWarningTemperture();
extern void SetTempByKeyNum(unsigned char keyNum);
/*按鍵驅動函數,檢測按鍵動作*/
void KeyDrive()
{
static unsigned char keybackup[4][4] = {1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1};//按鍵備份值
unsigned char i, j;
for (i = 0; i < 4; i++) //行
{
for (j = 0; j < 4; j++) //列
{
if (keySta[i][j] != keybackup[i][j]) //按鍵兩次的值不相同,按鍵有動作
{
//由上一次的按鍵備份值確定這一次的按鍵狀態值
if (keybackup[i][j] != 0)
{
//按鍵按下
KeyAction(keyCodeMap[i][j]);//傳遞相應的按鍵碼值,驅動按鍵動作
}
keybackup[i][j] = keySta[i][j];//備份按鍵的值
}
}
}
}
void IrdKeyDrive()
{
unsigned char i = 0;
{
if (IrdCode[2] == IrdKeyCode[i][0]) //IrdKeyCode[i][0]為紅外鍵碼,IrdKeyCode[i][1]為對應的PC鍵盤編碼
{
//在表中找到對應的鍵碼
KeyAction(IrdKeyCode[i][1]); //執行相應的動作
break;
}
}
}
void LCD1602RefreshCoursor()
{
switch (setTempIndex)
{
case 1: LCD1602SetCoursor(8, 0); break;//shangxianLow的十位數字
case 2: LCD1602SetCoursor(9, 0); break; //shangxianLow的個位數字
case 3: LCD1602SetCoursor(11, 0); break; //shangxianHigh的十位數字
case 4: LCD1602SetCoursor(12, 0); break;
case 5: LCD1602SetCoursor(8, 1); break;
case 6: LCD1602SetCoursor(9, 1); break;
case 7: LCD1602SetCoursor(11, 1); break;
case 8: LCD1602SetCoursor(12, 1); break;
default: break;
}
OpenCoursor();//打開光標
}
void LCDCoursorLeft()
{
if (setTempIndex > 1)
{
setTempIndex--;
}
else
{
setTempIndex = 8;
}
LCD1602RefreshCoursor();//刷新光標顯示
}
void LCDCoursorRight()
{
if (setTempIndex < 8)
{
setTempIndex++;
}
else
{
setTempIndex = 1;
}
LCD1602RefreshCoursor();//刷新光標顯示
}
void KeyAction(unsigned char keycode)
{
if (keycode >= '0' && keycode <= '9')
{
if (setTempIndex != 0) //處于設置狀態
{
SetTempByKeyNum(keycode - '0'); //0-9
}
}
else if (keycode == 0x25) //<-
{
if (setTempIndex != 0) //處于設置狀態時響應
{
LCDCoursorLeft();
}
}
else if (keycode == 0x27) //->
{
if (setTempIndex != 0)
{
LCDCoursorRight();
}
}
else if (keycode == 0x26) //UP
{
if (setTempIndex != 0) //處于設置狀態時響應
{
IncCoursorNum();
}
}
else if (keycode == 0x28)//Down
{
if (setTempIndex != 0)
{
DecCoursorNum();
}
}
else if (keycode == 0x1B) //ESC
{
setTempIndex = 0;
CloseCoursor();
}
else if (keycode == 0x0D) //Enter
{
if (setTempIndex == 0)
{
setTempIndex = 1;
LCD1602RefreshCoursor();
}
else
{
setTempIndex = 0;
SetWarningTemperture();//設定報警溫度值,并將設定的報警值保存到eeprom中
CloseCoursor();
}
}
}
void KeyScan()
{
static unsigned char keyout = 0;//按鍵行索引
static unsigned char keybuff[4][4] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
//16個按鍵的掃描緩沖區
unsigned char i = 0;
//掃描一行按鍵,將掃描值存入keybuff中
keybuff[keyout][0] = (keybuff[keyout][0] << 1) | KEY_IN_1;
keybuff[keyout][1] = (keybuff[keyout][1] << 1) | KEY_IN_2;
keybuff[keyout][2] = (keybuff[keyout][2] << 1) | KEY_IN_3;
keybuff[keyout][3] = (keybuff[keyout][3] << 1) | KEY_IN_4;
//更新消抖后的按鍵狀態值
for (i = 0; i < 4; i++)
{
//每行有四個按鍵
if ((keybuff[keyout][i] & 0x1F) == 0x00) //五次檢測都是0
{
keySta[keyout][i] = 0;//按鍵按下
}
else if ((keybuff[keyout][i] & 0x1F) == 0x1F)//五次都是1
{
keySta[keyout][i] = 1;//按鍵彈起
}
}
//檢測下一行按鍵
keyout++;
keyout &= 0x03;//0x11按位與實現到4歸零
//根據keyout的值來選擇檢測哪一行
switch (keyout)
{
case 0: KEY_OUT_1 = 0; KEY_OUT_4 = 1; break;
case 1: KEY_OUT_2 = 0; KEY_OUT_1 = 1; break;
case 2: KEY_OUT_3 = 0; KEY_OUT_2 = 1; break;
case 3: KEY_OUT_4 = 0; KEY_OUT_3 = 1; break;
default: break;
}
}
/**
***********************************************************************************************
* @file : I2C.c
* @author : xr
* @date : 2014年4月28日19:18:15
* @version : V1.2.3
* @brief : I2C通信底層驅動
***********************************************************************************************
*/
#include <reg52.h>
#include <intrins.h>
sbit SDA = P3^6;
sbit SCL = P3^7;
#define nops() { _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); } //延時大約5us
/*I2C起始信號*/
void I2CStart()
{
SDA = 1;
SCL = 1; //先拉高SDA和SCL
nops();
SDA = 0;//拉低SDA,在SCL為高電平期間SDA1-0的跳變被當做I2C的起始信號
nops();
SCL = 0;//拉低SCL,準備好發送數據
}
void I2CStop()
{
SDA = 0;
SCL = 0; //首先確保SDA和SCL為低電平,即忙碌
nops();
SCL = 1; //拉高SCL
nops();
SDA = 1;//在SCL為高電平期間SDA0-1的變化被當做是停止信號
nops();
}
bit I2CWriteByte(unsigned char byte)
{
bit ack = 0;
unsigned char mask = 0x80;//從高位開始發送
//I2CStart中已將SCL拉低,可以直接發送數據了
for (mask = 0x80; mask != 0; mask >>= 1)
{
if ((byte & mask) != 0) //送入數據
{
SDA = 1;//發送1
}
else
{
SDA = 0;//發送0
}
nops();//等待數據發送完成
SCL = 1;//拉高SCL使數據線SDA穩定不變
nops();
SCL = 0;//等待下一位的發送
}
/*主機釋放SDA數據線,從機開始發送應答ack*/
SDA = 1;
nops();
SCL = 1; //拉高SCL以接收從機應答
ack = SDA;//接收從機應答ack
nops();
SCL = 0;//拉低SCL等待下一次的數據發送
return (~ack); //ack==0表示寫成功
}
//unsigned char I2CReadByteAck()
//{
// unsigned char byte = 0;
// unsigned char mask = 0x80;//高位在先,逐位接收
//
// //主機釋放SDA數據線
// SDA = 1;
// //I2CStart中已將SCL拉低,等待從機發送數據
// for (mask = 0x80; mask != 0; mask >>= 1)
// {
// nops();//延時>4.7us等待從機將數據發送到I2C總線上
// SCL = 1;//拉高SCL使SDA線上的數據穩定
// if (SDA != 0)
// {
// byte |= mask;//相應位置1
// }
// else
// {
// byte &= (~mask);//相應位清零
// }
// nops();//等待數據讀取成功
// SCL = 0;//拉低SCL等待從機發送下一位數據
// }
// SDA = 0;//主機發送應答位0,繼續接收數據
// nops();
// SCL = 1;//拉高SCL從機等待主機的應答
// nops();
// SCL = 0;//最后拉低SCL以進行下一次數據的發送和接收
//
// return (byte);
//}
unsigned char I2CReadByteNAck()
{
unsigned char byte = 0;
unsigned char mask = 0x80;//高位在前,逐位接收
//主機先釋放SDA等待從機發送數據
SDA = 1;
//I2CStart起始信號已將SCL拉低,從機開始發送數據
for (mask = 0x80; mask != 0; mask >>= 1)
{
nops();//等待從機將數據發送到I2C總線上
SCL = 1;//使SDA上的數據穩定
if (SDA != 0)
{
byte |= mask;
}
else
{
byte &= (~mask);
}
nops();//等待主機采樣數據成功
SCL = 0;//等待從機發送下一位數據
}
SDA = 1;//主機發送非應答1,停止接收數據
nops();
SCL = 1;//拉高SCL從機以等待主機的非應答信號
nops();
SCL = 0;//拉低SCL等待下一次的I2C通信
return (byte);
}
/**
***********************************************************************************************
* @file : eeprom.c
* @author : xr
* @date : 2014年4月28日19:18:15
* @version : V1.2.3
* @brief : I2C通信協議讀寫eeprom存儲器件
***********************************************************************************************
*/
#include <reg52.h>
#define EEPROM_ADDR 0x50 //1010 A2A1A0 七位器件地址
/*I2C底層驅動*/
extern void I2CStart();
extern void I2CStop();
extern bit I2CWriteByte(unsigned char byte);
//extern unsigned char I2CReadByteAck();
extern unsigned char I2CReadByteNAck();
/*向EEPROM的addr地址寫一個字節的數據*/
void WriteEEPROMByte(unsigned char addr, unsigned char dat)
{
do
{
I2CStart();
if (I2CWriteByte(EEPROM_ADDR << 1)) //寫eeprom器件地址ADDR并在讀寫方向上選擇寫,從機回復應答ack
{
break;//尋址成功強制退出
}
I2CStop();
} while (1);
I2CWriteByte(addr);//寫入數據地址addr,并返回應答ack
I2CWriteByte(dat); //寫入數據dat
I2CStop();
}
unsigned char ReadEEPROMByte(unsigned char addr)
{
unsigned char dat = 0;
/*I2C進行EEPROM尋址*/
do
{
I2CStart();
if (I2CWriteByte(EEPROM_ADDR << 1)) //寫入器件地址ADDR并選擇寫
{
break;
}
I2CStop();
} while (1);
I2CWriteByte(addr); //寫入要讀取的數據的地址addr
I2CStart();
I2CWriteByte((EEPROM_ADDR << 1) | 0x01); //寫器件地址ADDR并在讀寫方向上選擇讀
dat = I2CReadByteNAck();//讀取數據,主機返回非應答
I2CStop();
return (dat);
}
/**
******************************************************************************
* @file : Infrared.c
* @author : xr
* @date : 2014年5月1日20:57:45
* @version : V1.2.3
* @brief : 紅外通信-基于NEC協議的底層驅動
******************************************************************************
*/
sbit IRD = P3^3;//外部中斷1引腳
unsigned char IrdCode[4];//裝載紅外解碼值
void ConfigInfrared()
{
TMOD &= 0x0F;//清零T1控制位
TMOD |= 0x10;//十六位計數器模式
TH1 = 0;
TL1 = 0;//清零T1計數器
TR1 = 0;//先關閉T1計數
ET1 = 0;//不用T1中斷
//配置外部中斷
IE1 = 0;//外部中斷1標志位清零
IT1 = 1;//設置外部中斷1的觸發方式是下降沿觸發(0:低電平觸發,1:下降沿觸發)
EX1 = 1;//開啟外部中斷1
}
unsigned int GetHighTimers()
{
IRD = 1;//檢測該引腳之前要先釋放IRD引腳
TH1 = 0;
TL1 = 0;//每次都要清零T1計數值
TR1 = 1;//打開T1計數
{
//超時判斷,超過17ms認為是誤碼,強制退出 TL1加滿256就向TH1進1
if (TH1 > 0x40) //(0x40*256)*12/11059200(s)= 17.8ms
{
break;
}
}
TR1 = 0;//關閉T1計數
}
unsigned int GetLowTimers()
{
IRD = 1;//檢測IRD引腳之前要先將IRD引腳拉高釋放
TH1 = 0;
TL1 = 0;//清零T1計數值
TR1 = 1;//開啟T1計數
{
if (TH1 > 0x40) //超過17ms表示是誤碼,強制退出
{
break;
}
}
TR1 = 0;//關閉T1計數
}
void EX1_ISP() interrupt 2
{
static unsigned int time = 0;
unsigned char i = 0, j = 0;
unsigned char Irbyte = 0;
time = GetLowTimers();//獲取載波
if ((time < 7833) || (time > 8755)) //8.5ms-9.5ms time = T1計數值*(12/11059200)(s)
{
IE1 = 0;//清零外部中斷標志
return;//退出中斷服務
}
time = GetHighTimers();//獲取空閑
if ((time < 3686) || (time > 4608)) //4ms-5ms
{
IE1 = 0;
return;
}
//引導碼正確,開始接收用戶碼和鍵碼
/*比特值0:560us載波+560us空閑 比特值1:560us載波+1.68us空閑*/
for (i = 0; i < 4; i++) //用戶碼+用戶反碼+鍵碼+鍵碼反碼
{
for (j = 0; j < 8; j++) //解碼值是八位數據
{
time = GetLowTimers();//載波 時間范圍:360us-760us
if ((time < 332) || (time > 700))
{
IE1 = 0;
return;
}
//560us載波正確
time = GetHighTimers();//空閑
if ((time > 332) && (time < 700)) //'0'
{
//接收比特0
Irbyte >>= 1;//低位在先,逐位接收
}
else if ((time > 1290) && (time < 1658)) //1.68ms空閑,范圍:1.4ms-1.8ms
{
//接收比特1
Irbyte >>= 1; //先將這一位移出來
Irbyte |= 0x80;//再將此位置1
}
else
{
//誤碼
IE1 = 0;
return;
}
}
//接收一個字節
IrdCode[i] = Irbyte;
}
flagIrd = 1;//接收完成標志置1
IE1 = 0;//清零外部中斷標志
}