功能实现:
1、设计单片机与16按键的矩阵式键盘接口以及8数码管的接口电路,测试显示和按键功能。
2、设计单片机与DS18B20的接口电路,实现数字温度信息的采集,然后编程处理采集到的数据,得到温度值。
3、将采集的温度信息的显示在数码管上。
4、设计声光报警电路,设置温度的上下限值,实现报警功能
5,将温度上下限显示在数码管上。
protues仿真图
代码较长分开说明:
一、 定义与函数声明
。
#include <reg52.h>
#include <intrins.h> // 因为此文件中用到了延时函数_nop_()(延时一个机器周期),所以要包含_nop_()的头文件、
#define u16 unsigned int
#define u8 unsigned char
char temperatureH = 100, temperatureL = -10; //最高温,最低温 char范围-128~127
// 键盘定义
u8 line, row, Sort = 0, kvalue = 0, kscan, kscanA = 0, kscanB = 0;
u8 keyRowLine();
void getKeyVaule();
void setH();
void setL();
void showH();
void showL();
void dealKey();
// 测温定义
sbit SPEAK = P3 ^ 1;
sbit DQ = P3 ^ 7;
u8 flag = 0, EX = 0, setting = 0, led_active = 0;
u16 n = 0;
u8 dat[10] = {0, 0, 0, 12, 5, 6, 7, 8, 9};
u8 dispcode[] =
{0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,
0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71,
0x40, 0x76, 0xb8, 0xb7, 0xbe}; //'-' 'H' 'L' 'n' 'u'
// *****************函数声明*******************
void delay5(u8);
u8 initialize();
void write1Byte(u8 come);
u8 read1Byte();
u16 getTemperature();
void Temp_treatment();
void LED_4();
void alarm();
唯一要注意的是延时函数_nop_()为延时一个机器周期的作用
二、代码逻辑概括 主函数
// *****************主函数**********************
void main()
{
u8 i;
delay5(1000);
while (1)
{
getKeyVaule(); //键盘函数
dealKey(); //处理按键
Temp_treatment(); //温度函数
for (i = 0; i < 30; i++)
{
LED_4(); //显示LED
delay5(1000);
if (EX)
{
alarm(); //报警
delay5(1000);
}
}
}
}
for (i = 0; i < 30; i++) 语句是为了在一次while (1)循环中LED显示次数增多,增强LED显示稳定性。
EX代表温度超过范围,会使蜂鸣器报警。
三、精确延时5us 延时函数
。
// *****************延时函数********************
void delay5(u8 n)
{
do
{ //搭配11.059MHZ 精确延时5us
_nop_();
_nop_();
_nop_();
n--;
} while (n);
}
四、51单片机想向18B20打招呼 初始化函数
// *****************初始化函数******************
u8 initialize()
{
u8 Prence;
DQ = 0; //拉低总线
delay5(120); //600us
DQ = 1; //总线控制给18b20
delay5(16); //80us
if (DQ)
{
Prence = 0;
}
else
{
Prence = 1;
}
delay5(80); //400us
return Prence;
}
初始化时序图
黑色实线: 51控制总线 灰色实线: 18B20控制总线
Prence为设定的标志位,防止无人应答进入死循环。
五、51单片机向18B20发指令 写函数
// *****************写函数**********************
void write1Byte(u8 come)
{
u8 i;
for (i = 0; i < 8; i++)
{
DQ = 0; //51控制总线
DQ = come & 0x01; //低位开始写,1us
delay5(12); //60us
DQ = 1; //18b20控制总线
come = come >> 1;
delay5(5);
}
}
写时序图
无论读写,都是从低字节,低位开始的。
六、51单片机从18B20读取温度 读函数
// *****************读函数**********************
u8 read1Byte()
{
u8 i, value = 0;
for (i = 0; i < 8; i++)
{
DQ = 0; //51控制总线
delay5(1); //5us
DQ = 1; //释放总线
value = value >> 1; //从低位开始
_nop_(); //一个机器周期的时间
if (DQ)
{
value = value | 0x80;
}
delay5(11);
}
return value;
}
读时序图
if (DQ)
{
value = value | 0x80;
}
整个函数中这一句话是最难理解的,指当18B20传来的数为1时,执行语句,为0时不执行,毕竟51和18B20只有DQ连着,所以18B20传什么,DQ就是什么。直接举个栗子。
// DS18B20 1110 0011 0x80=1000 0000
// 51 i=0 1111 0001 i=1
// 51 1111 1000 i=2
// 51 0111 1100 i=3
// 51 0011 1110 i=4
// 51 0001 1111 i=5
// 51 1000 1111 i=6
// 51 1100 0111 i=7
// 51 1110 0011 i=8
经过一个读函数,51成功读到了18B20的一个字节。
七、准备工作完成开始 测温
// *****************测温函数******************
u16 getTemperature()
{
// 启动温度转换
u8 low, high, Temp;
initialize();
delay5(10);
write1Byte(0xcc);//单片工作
write1Byte(0x44);//启动温度转换
initialize();
write1Byte(0xcc);
write1Byte(0xbe);//读取
low = read1Byte();
high = read1Byte();
Temp = (high << 4) | (low >> 4);
if (Temp > 127)
{ //温度为负值,将补码转换成原码
Temp = ~Temp + 1;
flag = 1;
}
else
{
flag = 0;
}
return (Temp);
}
解释一下下面语句如何获得温度的整数值
Temp = (high << 4) | (low >> 4);
//和
if (Temp > 127)
{ //温度为负值,将补码转换成原码
Temp = ~Temp + 1;
flag = 1;
}
首先,温度的数据放在DS18B20的前2个字节
所以,low位温度值低位,high为温度值高位。
这2个字节各个位的含义:
// 2字节 16位 从低位开始
// 1~4位 温度的小数部分
// 5~11位 温度的整数部分
// 12~16位 符号位
Temp = (high << 4) | (low >> 4);
//0111 0000 | 0000 1101= 0111 1101
// 即十进制的125
//如果Temp为正数,Temp最大为 0111 1111 即127。补码就是原码
//Temp > 127,则温度为负数,符号位为1,将补码转换成原码
求小数部分,这个实验中没用到,拓展一下
/
delay5(1000);
line = P2;
if (line != 0x0f)
{
line = line & 0x0f;
P2 = 0xf0;
// delay1ms(1); //等待变化时间
row = P2;
row = row & 0xf0;
kscan = line | row;
before = P2;
while (P2 == before)
{
}
return kscan;
}
}
return 0xff;
}
下面这个语句有利有弊,可以防止按一次键盘,产生多次按下的效果,但是按键按下时,LED会黑一下。自己权衡吧。
before = P2;
while (P2 == before)
{
}
根据按键调整标志位
// 获得键编码,调整标志位
void getKeyVaule()
{
u8 Kscan;
Kscan = keyRowLine();
switch (Kscan)
{
case 0xee:
setting = 1; //显示最低温
break;
case 0xde:
setting = 2; //显示最高温
break;
case 0xbe:
setting = 3; //设置最低温
break;
case 0x7e:
setting = 4; //设置最高温
break;
case 0xed: //归零
dat[0] = 0;
dat[1] = 0;
dat[2] = 0;
dat[3] = 12;
Sort = 0;
break;
case 0xdd: //输入负号
dat[0] = 16;
Sort = 1;
break;
case 0xbd:
dat[Sort] = 0;
Sort++;
break;
case 0x7d:
dat[Sort] = 1;
Sort++;
break;
case 0xeb:
dat[Sort] = 2;
Sort++;
break;
case 0xdb:
dat[Sort] = 3;
Sort++;
break;
case 0xbb:
dat[Sort] = 4;
Sort++;
break;
case 0x7b:
dat[Sort] = 5;
Sort++;
break;
case 0xe7:
dat[Sort] = 6;
Sort++;
break;
case 0xd7:
dat[Sort] = 7;
Sort++;
break;
case 0xb7:
dat[Sort] = 8;
Sort++;
break;
case 0x77:
dat[Sort] = 9;
Sort++;
break;
default:
break;
}
if (Sort >= 3)
{
Sort = 0;
}
}
Sort取值0~2,使输入在数码管1 ~3 位循环
按键响应函数
// *****************按键响应函数********************
void dealKey()
{
if (setting == 1)
{
showL();//显示温度下限
}
else if (setting == 2)
{
showH();//显示温度上限
}
else if (setting == 3)
{
setL();//设定温度下限
}
else if (setting == 4)
{
setH();//设定温度上限
}
}
设定温度上下限函数差不多,这里就说说设定温度上限函数
void setH()
{
temperatureH = 0;
temperatureH = 10 * dat[1] + dat[2];
if (dat[0] == 16)
{
temperatureH = 0 - temperatureH;
}
else
{
temperatureH = 100 * dat[0] + temperatureH;
}
Sort = 0;//*
setting = 0;//*
dat[3] = 17;
}
首先判断最高位是不是负号,然后将数码管的值赋给全局变量temperatureH即可。
Sort = 0;//*
setting = 0;//*
这两句话非常重要,setting也是全局变量,如果不置成0,按键响应函数将不断重复上一次执行的功能。Sort = 0;是为了保证,按键响应后,键盘输入从数码管第一位开始。
如果设置温度上限成功,数码管第4位(输入状态)会显示‘H’
//************显示温度下限***************
void showL()
{
char LowTemperature = temperatureL;//*
if (LowTemperature < 0)
{
LowTemperature = 0 - LowTemperature;
dat[0] = 16;
dat[1] = LowTemperature / 10;
dat[2] = LowTemperature % 10;
}
else
{
dat[0] = LowTemperature / 100;
dat[1] = (LowTemperature / 10) % 10;
dat[2] = LowTemperature % 10;
}
Sort = 0;
setting = 0;
dat[3] = 20;
}
本函数需要注意的语句
char LowTemperature = temperatureL;//*
本函数首先会让负数转为正数,temperatureL是全局变量,如果直接用temperatureL进行showL()操作,可能改变设置好的温度下限。
因此,采用局部变量LowTemperature对temperatureL进行替换,局部变量的改变不会影响全局变量。
最后显示成功如图(U的上面开口,下面封闭,用来表示下限,同理‘n’表示上限。
暂时就这样吧!