51单片机从零开始入门教程(传感器篇)

参考教程:[13-1] DS18B20温度传感器_哔哩哔哩_bilibili

1、DS18B20是一种常见的数字温度传感器,其控制命令和数据都是以数字信号的方式输入输出,相比较于模拟温度传感器,具有功能强大、硬件简单、易扩展、抗干扰性强等特点。

(1)测温范围:-55°C 到 +125°C。

(2)通信接口:1-Wire(单总线)

(3)其它特征:可形成总线结构、内置温度报警功能、可寄生供电。

2、引脚及其应用电路:

引脚

功能

VCC

电源(3.0V~5.5V)

GND

电源地

I/O(DQ)

单总线接口

3、内部结构框图:

(1)64-BIT ROM:作为器件地址,用于总线通信的寻址。

(2)SCRATCHPAD(暂存器):用于总线的数据交互。

(3)EEPROM:用于保存温度触发阈值和配置参数。

4、单总线(1-Wire BUS)是由Dallas公司开发的一种通用数据总线,它只需要一根通信线DQ(异步、半双工)即可实现数据的双向传输,当采用寄生供电时,还可以省去设备的VDD线路,此时,供电加通信只需要DQ和GND两根线。

5、单总线电路规范:

(1)设备的DQ均要配置成开漏输出模式。

(2)DQ添加一个上拉电阻,阻值一般为4.7KΩ左右。

(3)若此总线的从机采取寄生供电,则主机还应配一个强上拉输出电路。

6、单总线时序结构:

(1)初始化:主机将总线拉低至少480us,然后释放总线,等待15~60us后,存在的从机会拉低总线60~240us以响应主机,之后从机将释放总线。

(2)发送一位:主机将总线拉低60~120us,然后释放总线,表示发送0;主机将总线拉低1~15us,然后释放总线,表示发送1。从机将在总线拉低30us后(典型值)读取电平,整个时间片应大于60us。

(3)接收一位:主机将总线拉低1~15us,然后释放总线,并在拉低后15us内读取总线电平(尽量贴近15us的末尾),读取为低电平则为接收0,读取为高电平则为接收1 ,整个时间片应大于60us。

(4)发送一个字节:连续调用8次发送一位的时序,依次发送一个字节的8位(低位在前)。

(5)接收一个字节:连续调用8次接收一位的时序,依次接收一个字节的8位(低位在前)。

7、DS18B20操作流程:

(1)初始化:从机复位,主机判断从机是否响应。

(2)ROM操作:ROM指令+本指令需要的读写操作。

(3)功能操作:功能指令+本指令需要的读写操作。

ROM指令

功能指令

SEARCH ROM [F0h]

CONVERT T [44h]

READ ROM [33h]

WRITE SCRATCHPAD [4Eh]

MATCH ROM [55h]

READ SCRATCHPAD [BEh]

SKIP ROM [CCh]

COPY SCRATCHPAD [48h]

ALARM SEARCH [ECh]

RECALL E2 [B8h]

READ POWER SUPPLY [B4h]

8、DS18B20数据帧:

(1)温度变换:初始化→跳过ROM →开始温度变换。

(2)温度读取:初始化→跳过ROM →读暂存器→连续的读操作。(本例只要读SCRATCHPAD的前两个字节即可)

注:什么是“跳过ROM”?见上面的内部结构框图,SCRATCHPAD前接了一个ROM,跳过ROM就是直接来到SCRATCHPAD(在本例中只有SCRATCHPAD一个从机,要是后面还有其它从机就不能这样简单跳过)。

9、温度存储格式:S代表温度的正负(温度为负数,S为1),剩下的全都表示温度的数值(按二进制补码形式存储,存储在SCRATCHPAD的Byte0和Byte1,也就是前两个字节)。

10、DS18B20温度读取:

(1)项目包含的文件:其中需要重写的都会在下面给出,未给出的沿用旧例出现过的即可(本例需要液晶屏模块的代码文件以及延时函数的代码)。

(2)补充缺失的代码文件,进行编译。(OneWire.c文件中有很多不同的延时代码,一定要结合时序图决定延迟多长时间,代码可以使用STC-ISP生成,在误差范围内延迟时间不一定要非常精确

①OneWire.h文件:

#ifndef __OneWire_H__
#define __OneWire_H__

unsigned char OneWire_Init();
void OneWire_SendBit(bit Bit);
bit OneWire_ReceiveBit();
void OneWire_SendByte(unsigned char Byte);
unsigned char OneWire_ReceiveByte();

#endif

②OneWire.c文件:

#include <REGX52.H>

sbit OneWire_DQ = P3^7;

unsigned char OneWire_Init()
{
	unsigned char i, AckBit;
	OneWire_DQ = 1;
	OneWire_DQ = 0;   //拉低总线至少480us
	i = 227;
	while (--i);    //Delay 500us
	
	OneWire_DQ = 1;	  //主机释放总线,等待60us,从机会拉低总线60~240us
	i = 29;
	while (--i);    //Delay 70us
	
	AckBit = OneWire_DQ;  //读取从机应答
	i = 227;
	while (--i);    //Delay 500us(等从机释放总线)
	return AckBit;
}

void OneWire_SendBit(bit Bit)
{
	unsigned char i;
	OneWire_DQ = 0;  //主机先将总线拉低15us
	i = 3;
	while (--i);    //Delay 10us
	
	OneWire_DQ = Bit;  //如果发生的Bit为1,释放总线,否则继续拉低总线,从机会在这50us内读走数据
	i = 22;
	while (--i);    //Delay 50us
	
	OneWire_DQ = 1;  //主机释放总线
}

bit OneWire_ReceiveBit()
{
	unsigned char i;
	bit Bit;
	OneWire_DQ = 0;  //主机先将总线拉低1-15us
	i = 1;
	while (--i);    //Delay 5us
	
	OneWire_DQ = 1;  //主机释放总线
	i = 1;
	while (--i);    //Delay 5us
	
	Bit = OneWire_DQ;  //主机拉低总线后的15us内要读取数据
	i = 22;
	while (--i);    //Delay 50us(时间片要大于60us)
	
	return Bit;
}

void OneWire_SendByte(unsigned char Byte)
{
	unsigned char i;
	for(i = 0; i < 8; i++)
	{
		OneWire_SendBit(Byte&(0x01<<i));
	}
}

unsigned char OneWire_ReceiveByte()
{
	unsigned char i,Byte = 0x00;
	for(i = 0; i < 8; i++)
	{
		if(OneWire_ReceiveBit())
		{
			Byte |= (0x01<<i);
		}
	}
	return Byte;
}

③DS18B20.h文件:

#ifndef __DS18B20_H__
#define __DS18B20_H__

void DS18B20_ConvertT();
float DS18B20_ReadT();

#endif

④DS18B20.c文件:

#include <REGX52.H>
#include "OneWire.h"

#define DS18B20_SKIP_ROM        0xCC
#define DS18B20_Convert_T       0x44
#define DS18B20_READ_SCRATCHPAD 0xBE

void DS18B20_ConvertT()
{
	OneWire_Init();  //初始化
	OneWire_SendByte(DS18B20_SKIP_ROM);  //跳过ROM
	OneWire_SendByte(DS18B20_Convert_T); //发送指令:开始温度变换(测量当前温度)
}

float DS18B20_ReadT()
{
	unsigned char TLSB,TMSB;
	int Temp;
	float T;
	
	OneWire_Init();  //初始化
	OneWire_SendByte(DS18B20_SKIP_ROM);  //跳过ROM
	OneWire_SendByte(DS18B20_READ_SCRATCHPAD);  //发送指令:读暂存器中的温度数据
	TLSB = OneWire_ReceiveByte();  //读取LSB
	TMSB = OneWire_ReceiveByte();  //读取MSB(具体见内部结构框图的SCRATCHPAD)
	
	Temp = (TMSB<<8)|TLSB;  //将LSB和MSB拼接在一起得到完整的温度数据(二进制补码,有对应的十进制数字)
	T = Temp/16.0;    //测量值转成实际温度(16.0是经过多次测试而得的)
	return T;
}

⑤main.c文件:

#include <REGX52.H>
#include "LCD1602.h"
#include "Delay.h"
#include "DS18B20.h"

float T;

void main()
{
	DS18B20_ConvertT();  //先转换一次温度,这样就不会读到SCRATCHPAD中的默认温度值
	Delay(1000);
	LCD_Init();
	LCD_ShowString(1,1,"Temperature");
	
	while(1)
	{
		DS18B20_ConvertT();   //测当前温度
		T = DS18B20_ReadT();  //获取温度值
		if(T<0)  //判断温度的正负
		{
			LCD_ShowChar(2,1,'-');
			T = -T;
		}
		else
		{
			LCD_ShowChar(2,1,'+');
		}
		LCD_ShowNum(2,2,T,3);
		LCD_ShowChar(2,5,'.');
		LCD_ShowNum(2,6,(unsigned long)(T*10000)%10000,4);  //显示温度T的小数部分(显示到小数点后4位)
	}
}

(3)将生成的.hex文件下载到开发板中,观察液晶屏的现象。

11、DS18B20温度报警器:

(1)项目包含的文件:其中需要重写的都会在下面给出,未给出的沿用旧例出现过的即可(本例需要液晶屏模块、独立按键模块(定时器扫描版)、存储器模块、I2C模块、定时器0模块、温度传感器模块的代码文件以及延时函数的代码)。

(2)补充缺失的代码文件,进行编译。

①OneWire.c文件:(主要是加入了屏蔽中断,防止时序被打乱

#include <REGX52.H>

sbit OneWire_DQ = P3^7;

unsigned char OneWire_Init()
{
	unsigned char i, AckBit;
	EA = 0;  //暂时屏蔽所有中断,防止延时函数被打乱(慎用!对定时器影响很大)
	
	OneWire_DQ = 1;
	OneWire_DQ = 0;   //拉低总线至少480us
	i = 227;
	while (--i);    //Delay 500us
	
	OneWire_DQ = 1;	  //主机释放总线,等待60us,从机会拉低总线60~240us
	i = 29;
	while (--i);    //Delay 70us
	
	AckBit = OneWire_DQ;  //读取从机应答
	i = 227;
	while (--i);    //Delay 500us(等从机释放总线)
	EA = 1;  //恢复中断
	return AckBit;
}

void OneWire_SendBit(bit Bit)
{
	unsigned char i;
	EA = 0;  //暂时屏蔽所有中断,防止延时函数被打乱
	
	OneWire_DQ = 0;  //主机先将总线拉低15us
	i = 3;
	while (--i);    //Delay 10us
	
	OneWire_DQ = Bit;  //如果发生的Bit为1,释放总线,否则继续拉低总线,从机会在这50us内读走数据
	i = 22;
	while (--i);    //Delay 50us
	
	EA = 1;  //恢复中断
	
	OneWire_DQ = 1;  //主机释放总线
}

bit OneWire_ReceiveBit()
{
	unsigned char i;
	bit Bit;
	EA = 0;  //暂时屏蔽所有中断,防止延时函数被打乱
	
	OneWire_DQ = 0;  //主机先将总线拉低1-15us
	i = 1;
	while (--i);    //Delay 5us
	
	OneWire_DQ = 1;  //主机释放总线
	i = 1;
	while (--i);    //Delay 5us
	
	Bit = OneWire_DQ;  //主机拉低总线后的15us内要读取数据
	i = 22;
	while (--i);    //Delay 50us(时间片要大于60us)
	
	EA = 1;  //恢复中断
	
	return Bit;
}

void OneWire_SendByte(unsigned char Byte)
{
	unsigned char i;
	for(i = 0; i < 8; i++)
	{
		OneWire_SendBit(Byte&(0x01<<i));
	}
}

unsigned char OneWire_ReceiveByte()
{
	unsigned char i,Byte = 0x00;
	for(i = 0; i < 8; i++)
	{
		if(OneWire_ReceiveBit())
		{
			Byte |= (0x01<<i);
		}
	}
	return Byte;
}

②main.c文件:

#include <REGX52.H>
#include "LCD1602.h"
#include "Delay.h"
#include "DS18B20.h"
#include "AT24C02.h"
#include "Key.h"
#include "Timer0.h"

float T,TShow;
char TLow, THigh;
unsigned char KeyNum;

void main()
{
	THigh = AT24C02_ReadByte(0);  //将上限值从存储器中读出
	TLow = AT24C02_ReadByte(1);   //将下限值从存储器中读出
	if(THigh > 125|| TLow < -55 || THigh <= TLow)  //如果存储器中的值非法,就赋予默认值
	{
		THigh = 20;
		TLow = 15;
	}
	DS18B20_ConvertT();  //先转换一次温度,这样就不会读到SCRATCHPAD中的默认值
	Delay(1000);
	LCD_Init();
	LCD_ShowString(1,1,"T:");
	LCD_ShowString(2,1,"TH:");
	LCD_ShowString(2,9,"TL:");
	Timer0_Init();
	
	while(1)
	{
		/*温度读取及显示*/
		DS18B20_ConvertT();   //测当前温度
		T = DS18B20_ReadT();  //获取温度值
		if(T<0)
		{
			LCD_ShowChar(1,3,'-');
			TShow = -T;
		}
		else
		{
			LCD_ShowChar(1,3,'+');
			TShow = T;
		}
		LCD_ShowNum(1,4,TShow,3);
		LCD_ShowChar(1,7,'.');
		LCD_ShowNum(1,8,(unsigned long)(TShow*100)%100,2);  //显示温度T的小数部分(显示到小数点后2位)
	
		/*阈值判断及显示*/
		KeyNum = Key();
		if(KeyNum)   //如果有按键按下
		{
			if(KeyNum == 1)  //按下按键1,上限值+1
			{
				THigh++;
				if(TLow > 125)  //温度传感器最高测到125摄氏度
					TLow = 125;
			}
			if(KeyNum == 2)  //按下按键2,上限值-1
			{
				THigh--;
				if(TLow >= THigh)  //下限不能大于等于下限
					THigh++;
			}
			if(KeyNum == 3)  //按下按键3,下限值+1
			{
				TLow++;
				if(TLow >= THigh)  //下限不能大于等于下限
					TLow--;
			}
			if(KeyNum == 4)  //按下按键4,下限值-1
			{
				TLow--;
				if(TLow < -55)  //温度传感器最低测到-55摄氏度
					TLow = -55;
			}
		}
		LCD_ShowSignedNum(2,4,THigh,3);  //显示上限值(带符号的三位数)
		LCD_ShowSignedNum(2,12,TLow,3);  //显示下限值(带符号的三位数)
		AT24C02_WriteByte(0,THigh);  //将上限值写进存储器
		Delay(5);
		AT24C02_WriteByte(1,TLow);  //将下限值写进存储器
		Delay(5);
		if(T > THigh)
		{
			LCD_ShowString(1,13,"OV:H");  //超出上限警告
		}
		else if(T < TLow)
		{
			LCD_ShowString(1,13,"OV:L");  //超出下限警告
		}
		else
		{
			LCD_ShowString(1,13,"    ");
		}
		
	}
}

void Timer0_Routine()  interrupt 1  //CPU响应中断后执行的函数
{
	static unsigned int T0Count = 0;  //定义计数器
	
	T0Count++;
	
	if(T0Count >= 20)  //每20个中断信号(20ms)执行一次下面的代码段(20ms正好是按键消抖需要的时间)
	{
		Key_Loop();
		T0Count = 0;
	}
	
	//每次中断结束都要重置计数单元
	TH0 = 0xFC;  //定时器0的计数单元高8位
	TL0 = 0x66;  //定时器0的计数单元低8位
}

(3)将生成的.hex文件下载到开发板中,根据main.c文件中的注释进行调试。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
学习51单片机C语言需要有一定的基础知识,首先要了解C语言的基本语法和编程规范。可以通过阅读相关的书籍和资料,网上视频教程来系统地学习C语言的基础知识。掌握了C语言的基础知识之后,就可以开始学习51单片机的相关知识。 其次,需要了解51单片机的硬件结构、指令集和编程环境。可以通过查阅51单片机的相关资料和学习笔记来了解51单片机的基本知识和编程环境的搭建。学习过程中可以通过实验和练习加深理解,掌握51单片机的基本原理和编程方法。 学习过程中还需要具备一定的动手能力和实践经验,可以通过购买一些实验套件进行实际的操作和编程练习。通过实际操作可以更加深入地理解单片机的工作原理和编程方法,同时也可以提高自己的动手能力和解决问题的能力。 另外,学习51单片机C语言还需要有一定的毅力和耐心,因为学习过程中可能会遇到各种各样的困难和问题,需要持之以恒地克服这些困难。可以多参与一些相关的社区和论坛,向其他有经验的人请教和交流,可以更快地解决问题和提高自己的学习效率。 总之,学习51单片机C语言需要持续地学习和实践,掌握C语言的基础知识、了解51单片机的硬件结构和编程环境,提高动手能力和解决问题的能力,同时要有毅力和耐心,相信通过不懈的努力一定能够掌握这门技术。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zevalin爱灰灰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值