此程序是基于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>
//74HC138
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
};
/**************************local values definition***********************************/
//温度状态
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内存
signed int temp;//存储温度值
bit buzzflag = 0;//蜂鸣器启动标志
//定义报警温度的上限值和下限值范围(以下温度值是*10之后的温度值)温度值分正负且都是int型
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 function definition***********************************/
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();
/*主函数main()*/
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(); //检测按键动作
if (flagIrd)
{
flagIrd = 0;
IrdKeyDrive();//红外按键检测
}
if (flag2s)
{
flag2s = 0;
if (ReadDS18B20Temperture(&temp))
{
TempToLedBuf(temp); //将温度值转换成数码管数字并检测温度是否超限,超限启动声光报警!
StartConvertTemp();//再次启动温度转换
}
}
}
}
/*将eeprom的存储的报警温度值的各个位数字读取到num[8]数组中*/
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] = ' ';
LcdShowStr(8, 0, str);
str[0] = num[2] + '0';
str[1] = num[3] + '0';
str[2] = ' ';
LcdShowStr(11, 0, str);
str[0] = num[4] + '0';
str[1] = num[5] + '0';
str[2] = ' ';
LcdShowStr(8, 1, str);
str[0] = num[6] + '0';
str[1] = num[7] + '0';
str[3] = ' ';
LcdShowStr(11, 1, str);
}
/*将温度值转换为有效数字存储到LedBuff中*/
void TempToLedBuf(signed int temp)
{
unsigned char buf[6];//缓冲区
signed char i = 0; //i必须是有符号型数据
if (temp < 0) //负温度值
{
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
}
/*刷新LCD设置位置上的数字显示*/
void RefreshLCDDisplay()
{
unsigned char str[3];//字符串缓冲区
switch (setTempIndex)
{
case 1: str[0] = num[0] + '0'; str[1] = ' '; LcdShowStr(8, 0, str); break;
case 2: str[0] = num[1] + '0'; str[1] = ' '; LcdShowStr(9, 0, str); break;//shangxianLow的十位和个位数字显示刷新
case 3: str[0] = num[2] + '0'; str[1] = ' '; LcdShowStr(11, 0, str); break;
case 4: str[0] = num[3] + '0'; str[1] = ' '; LcdShowStr(12, 0, str); break;
case 5: str[0] = num[4] + '0'; str[1] = ' '; LcdShowStr(8, 1, str); break;
case 6: str[0] = num[5] + '0'; str[1] = ' '; LcdShowStr(9, 1, str); break;
case 7: str[0] = num[6] + '0'; str[1] = ' '; LcdShowStr(11, 1, str); break;
case 8: str[0] = num[7] + '0'; str[1] = ' '; 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;
}
RefreshLCDDisplay();//刷新设置位置上的数字显示
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;
}
RefreshLCDDisplay();
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
}
}
/*配置T0定时器,由于刷新时间的要求,定时xms时间*/
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
}
}
/*配置蜂鸣器发声频率fr*/
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中断
}
/*定时器T0中断服务*/
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;
}
}
/*定时器T1中断服务配置蜂鸣器的发声频率*/
void Timer1_ISP() interrupt 3
{
TH1 = thr1;
TL1 = tlr1;
if (buzzflag)
{
BUZZ = ~BUZZ;//以frHZ驱动蜂鸣器发声
}
}
/**
***********************************************************************************************
* @file : lcd1602.c
* @author : xr
* @date : 2014年4月27日08:40:23
* @version : V1.2.3
* @brief : LCD1602驱动
***********************************************************************************************
*/
#include <reg52.h>
//LCD1602
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 tmpADDR0 = 0;
bit tmpADDR1 = 0;//地址选择缓冲区
/**************************local function definition***********************************/
void ContinueRefreshLED();
void LEDRefreshPause();
/*LCD1602忙碌等待*/
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则表示液晶忙碌,禁止操作
}
/*暂停LED扫描*/
void LEDRefreshPause()
{
ENLED = 1;//关闭LED使能
tmpADDR0 = ADDR0;//因为LED和LCD同时使用了P1^0和P1^1引脚,所以要暂时保存ADDR0和ADDR1的数据即LED扫描地址值
tmpADDR1 = ADDR1;
P0 = 0xFF; //数码管+LED小灯去抖动
}
/*继续扫描LED*/
void ContinueRefreshLED()
{
ADDR0 = tmpADDR0;
ADDR1 = tmpADDR1;//恢复原来LED扫描的地址选择值
ENLED = 0;//选择LED
P0 = 0xFF; //数码管和LED去抖
}
/*LCD1602写命令*/
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小灯)
}
/*LCD1602写数据*/
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);//关闭光标
}
/*写str字符串到液晶的坐标(x, y)上*/
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 != ' ')
{
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>
//DS18B20_IO
sbit DS18B20_IO = P3^2;//1ware总线引脚
/*DS18B20操作时间,延时x*10us*/
void DelayX10us(unsigned char x10us)
{
do
{
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_(); //大约延时10us
} while (x10us--);
}
/*复位DS18B20,读取DS18B20存在脉冲*/
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);
}
/*写一个字节数据到DS18B20总线上*/
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;//打开中断
}
/*从DS18B20读一个字节的数据*/
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); //返回接收到的数据
}
/*启动温度转换Convert Temperature*/
bit StartConvertTemp()
{
bit ack;
ack = ReadDS18B20Ack();//复位总线,并读取DS18B20存在脉冲
if (ack == 0) //读取到复位脉冲
{
WriteDS18B20Byte(0xCC);//跳过ROM寻址
WriteDS18B20Byte(0x44);//启动温度转换
}
return (~ack);//ack==0表示温度启动转换成功
}
/*读取温度值,将温度值保存到*temperture中,并返回应答ack*/
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>
//KEY
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,向右键
};
/*红外遥控器键码对应标准PC键盘编码,21个键码*/
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
};
/***************************local function definition************************************/
void KeyAction(unsigned char keycode);
/***************************extern function and values definition************************************/
extern unsigned char setTempIndex;
extern bit flagIrd; //红外解码完成标志
extern unsigned char IrdCode[4];//装载红外解码值
extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char * str);
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;
for (i = 0; i < 21; i++) //遍历红外键码表
{
if (IrdCode[2] == IrdKeyCode[i][0]) //IrdKeyCode[i][0]为红外键码,IrdKeyCode[i][1]为对应的PC键盘编码
{
//在表中找到对应的键码
KeyAction(IrdKeyCode[i][1]); //执行相应的动作
break;
}
}
}
/*刷新LCD1602光标显示*/
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();//刷新光标显示
}
/*按键动作函数,根据keycode的值执行相应的动作*/
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>
//I2C
sbit SDA = P3^6;
sbit SCL = P3^7;
/*I2C通信延时函数宏定义*/
#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,准备好发送数据
}
/*I2C停止信号*/
void I2CStop()
{
SDA = 0;
SCL = 0; //首先确保SDA和SCL为低电平,即忙碌
nops();
SCL = 1; //拉高SCL
nops();
SDA = 1;//在SCL为高电平期间SDA0-1的变化被当做是停止信号
nops();
}
/*向I2C通信总线发送一个字节数据,并返回从机应答信号ack*/
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表示写成功
}
///*从I2C总线上接收一个字节数据,主机返回ack 0,返回值:读取到的数据byte*/
//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);
//}
/*从I2C总线上接收一个字节的数据,主机返回非应答1,返回值:接收到的数据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;
}<