51单片机蓝牙温度检测风扇

1.蓝牙串口的使用

图中有两个同名的串行口缓冲寄存器,其中一个为发送寄存器SBUF,另一个为接收寄存器SBUF,两个寄存器便于单片机进行全双工方式进行通信。

串行发送数据时:SBUF写入数据。

串行接收数据时:SBUF发送数据。

当发送或接收完毕时,系统会自动产生中断信号,TI(发送中断)或RI(接收中断)。

 <1>串口的相关寄存器

波特率设置:

首先需要使用定时器1来设置波特率(在特定的时钟触发下,串行数据完成逐位传输的过程)。定时器1通常设置为8位自动重载定时器(当溢出时自动将TH1的值重装入TL1),这样就不用每一次都需要手动重置。

 

SCON寄存器

 

REN:允许/禁止串行接收控制位。由软件置位REN,当REN=1为允许串行状态接收状态,可启动串行接收器RxD,开始接收信息。REN=0为禁止接收。

51单片机的串行口共有4种工作方式。

小结:SM0和SM1是工作方式位的选择位,SM2为多机通信位主要用于方式2和方式3,默认为0.REN为允许接收位,REN=1时,允许串口接收;REN=0时,禁止接收。TB8和RB8用于多机通信,默认为0(这里就不详细介绍)。TI为发送完毕中断标志,RI为接收完毕中断标志,默认为0。

PCON寄存器:

SMOD:波特率选择位。当软件置位SMOD,SMOD=1,则串行通信方式1、2、3的波特率加倍;SMOD=0,则各工作方式的波特率加倍。复位时SMOD=0。

IE寄存器:

ES:串行口中断允许位,ES=1允许串口中断,ES=0禁止串口中断。 

<2>串口工作方式1的配置:

串口发送数据时,数据从TXD端输出,当TI=0时,从数据写入发送寄存器SBUF时,就启动串口数据的发送过程,自此串行口自动将起始位清零,然后发送数据位、奇偶校验位以及停止位。所有串行数据依次从TXD端发出,一帧数据发送完毕,使TXD端输出线保持在1的状态下,并将寄存器中的TI置为1,以便于查询数据是否发送完毕或作为发送中断请求信号。(发送完毕标志TI必须由软件清零)

串口设置步骤:

①设置定时器T1的工作方式

②计算定时器T1的初值(设置波特率)

③设置SCON寄存器

④设置PCON寄存器(是否倍速)

⑤中断设置(是否开启串口中断)

⑥启动定时器(TR1 = 1)

#include <REGX51.H>

void delay(unsigned int n)
{
	unsigned char i = 5;
	while(i--)
		while(n--);
}

void uart_init()
{
	SCON = 0x50;//8位UART,波特率可变
	PCON = 0x80;//倍频
	
	TMOD |= 0x20;//8位自动重载定时器,TH1的值自动装入TL1中
	TH1 = 0XFA;//波特率设置:9600波特率
	TR1 = 1;//定时器1运行控制位
}

void putchar(unsigned char n)//串口发送一个字节
{
	SBUF = n;
	while(!TI);
		TI = 0;
}

void puts(unsigned char *p)//串口发送字符串
{
	while(*p)
		putchar(*p++);
}

void main()
{
	uart_init();
	while(1)
	{
		puts("hello world!");
		delay(100);
	}
}

2.DS18B20模块的使用

概述:

        DS18B20数字温度计提供9位温度度数,指示器件的温度。

        信息经过单线接口送入DS18B20或从DS18B20送出,因此从中央处理器到DS18B20仅需连接一条线读、写和完成温度变换所需要的电源可以由数据线本身提供,而不需要外部电源。

        因为每一个DS18B20有唯一的系列号,因此多个DS18B20可以存在与同一条单总线上。这允许在许多不同的地方放置温度灵敏器件。

DS18B20特性:


1.独特的单总线接口,就需一条线则可实现双向通信(测温)

2.测温范围:-55℃~+125℃,可通过编程设定9—12位分辨率,对应分辨温度分别为0.5、0.25、0.125、0.0625℃。

3.支持多点组网(可连接多个DS18B20温度传感器),多个DS18B20可以并联(3或2线)实现多个组网测温,但注意超过8个要解决好供电问题,否则电压过低会导致传输不稳定,从而数据不准确。

4.工作电压:3.0~5.5V (寄生电源方式下可由数据线供电)

5.在使用过程中不需要外围电路,全部传感元件及转换电路都在芯片内了。(上拉电阻)

6.测温结果直接是数字量输出,单总线串行传送方式,同时可传送CRC校验码(校验数据采集是否正确),具有极强的抗干扰和纠错能力。

7.在9位分辨率时最多在93.75ms内把温度转换为数字,12位分辨率时最多在750ms内把温度值转换为数字。

8.负压特性:电源极性接反时,芯片不会因发热而烧毁, 但不能正常工作。

引脚说明:

由于DS18B20采用单总线协议,因此在编程的时候要严格按照时序进行。

 DS18B20每一次进行通信时都必须进行复位才能完成温度采集工作,下图为复位时序图:

按照复位时序,当DQ为高电平后,需保持480~960us。然后根据时序,释放总线,使DQ输出高电平,把控制权交给DS18B20,为了更稳定的实现复位,需要进行一定延时。

void DS18B20_rst()//ds18b20复位
{
	unsigned char x = 0;
	
	DQ = 1;				//DQ复位
	delay(5);			//稍做延时
	DQ = 0;				//单片机将DQ拉低
	delay(100);		//精确延时,大于480us
	DQ = 1;				//拉高总线
	delay(50);
	x = DQ;
	delay(50);
}

DS18B20的读取时序:

首先定义循环变量i,然后再定义变量dat保存接收到的数据,设置初值为0。进行8次循环读取一个字节。循环内部需要先将总线拉低15us以内,为了逐位接收数据,需要将数据右移一位,然后再将数据总线拉高。总线拉高后,对DQ引脚进行判断,如果DQ发送出来的是高电平,通过与运算将0x80与dat数据进行运算,就可以实现将dat最高位置为1。如果DQ是低电平,则保持dat不变,最后根据时序完成一次读取操作最少需要60us,进行延时。

unsigned char DS18B20_read()//ds18b20读取数据
{
	unsigned char i;
	unsigned char dat;
	for(i=0;i<8;i++)
	{
		DQ = 0;
		dat>>=1;
		DQ = 1;
		if(DQ)
			dat |= 0x80;
		delay(10);
	}
	return (dat);
}

DS18B20写入时序:

首先定义循环变量i循环8次。在循环中首先将总线拉低,再将要写入的数据发送到DQ端,该数值需要通过参数来实现,因此定义一个参数为date,然后再对date进行判断最低位状态,让date与0x01进行与运算,获取date最低位状态。当DQ获取新值后,需要根据传输的0还是1来确定延时时间。如果DQ是低电平,大约需要延时60us,如果是高电平,则需要延时一个很短时间。再传输完数值后,释放总线,将DQ恢复为高电平。然后将date右移一次,循环8次后,就可以把date逐位向DS18B20传输。 

void DS18B20_write(unsigned char date)//ds18b20写入数据
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		DQ = 0;
		DQ = date & 0x01;
		delay(10);
		date>>=1;
	}
}

DS18B20温度转换与显示

unsigned int temp_read()//读取温度并转换
{
	unsigned char a,b;
	
	DS18B20_rst();
	DS18B20_write(0xCC);//跳过读序列号
	DS18B20_write(0x44);//启动温度转换
	DS18B20_rst();
	DS18B20_write(0xCC);//跳过读序列号
	DS18B20_write(0xBE);//读取温度
	a = DS18B20_read();
	b = DS18B20_read();
	temp_value = b;
	temp_value <<= 8;		//高八位
	temp_value = temp_value | a;//高八位加上低八位=总温度
	
	if(temp_value < 0x0FFF)
		temp_flag = 0;
	else
	{
		temp_value = ~temp_value + 1;
		temp_flag = 1;
	}
	
	temp_value = temp_value * (0.625);//将温度扩大十倍,精确到一位小数
	
	return(temp_value);
}

void display()//温度显示
{
	unsigned char temp[5] = {0};
	
	temp[0] = temp_value/100 + '0';//转化为字符型
	temp[1] = temp_value%100/10 + '0';
	temp[2] = '.';
	temp[3] = temp_value%10 + '0';

	puts(temp);
	crlf();
}

温控风扇总代码:

#include <REGX51.H>
#include "lcd1602.h"

bit mark;
bit flag;

sbit DQ = P3^7;
sbit IN3 = P1^6;
sbit IN4 = P1^7;
sbit trig = P2^2;
sbit echo = P2^1;

extern void _nop_(void);

unsigned char temp_flag;
unsigned int temp_value;
unsigned int distance;


void delay_ds18b20(unsigned int i)//延时8us
{
	while(i--);
}

void delayus(unsigned char n)
{
	unsigned char i = n;
	while(i--)
		_nop_();
}

void delay(unsigned int n)
{
	unsigned char i,j;
	for(i=n;i>0;i--)
		for(j=133;j>0;j++);
}

void ds18b20_rst()
{
	unsigned char x;
	DQ = 1;
	delay_ds18b20(5);
	DQ = 0;
	delay_ds18b20(100);
	DQ = 1;
	delay_ds18b20(50);
	x=DQ;      
	delay_ds18b20(20);
}

unsigned char ds18b20_read()//读数据
{
	unsigned char i = 0;
	unsigned char Dat = 0;
	
	for(i=8;i>0;i--)
	{
		DQ = 0;			//给脉冲信号
		Dat >>= 1;	//读取下一位
		DQ = 1;			//给脉冲信号
		if(DQ)			
			Dat |= 0x80;//保存数据
		delay_ds18b20(10);//协议延时
	}
	return (Dat);
}

void ds18b20_wirte(unsigned char Data)//写数据
{
	unsigned char i=0;
	
	for(i=8;i>0;i--)
	{
		DQ = 0;						//给脉冲信号
		DQ = Data & 0x01;	//输出一位信号
		delay_ds18b20(10);//协议延时
		DQ = 1;						//给脉冲信号
		Data >>= 1;				//进行下一位输出
	}
}

unsigned int temp_read()
{
	unsigned char a,b;
	
	ds18b20_rst();
	ds18b20_wirte(0xCC);//跳过读序列号
	ds18b20_wirte(0x44);//启动温度转换
	ds18b20_rst();
	ds18b20_wirte(0xCC);//跳过读序列号
	ds18b20_wirte(0xBE);//读取温度
	a = ds18b20_read();
	b = ds18b20_read();
	temp_value = b;
	temp_value <<= 8;		//高八位
	temp_value = temp_value | a;//高八位加上低八位=总温度
	
	temp_value = temp_value * (0.625);//将温度扩大十倍,精确到一位小数
	
	return(temp_value);
}

void putchar(unsigned char n)//串口发送一个字节
{
	SBUF = n;
	while(!TI);
		TI = 0;
}

void puts(unsigned char *p)//串口发送字符串
{
	while(*p)
		putchar(*p++);
}

void crlf()
{
	putchar(0x0D);//回车
	putchar(0x0A);//换行
}

void T0_init()//初始化定时器T0
{
	TMOD |= 0x01;
	
	IE |= 0x8F;					//把所用中断打开
	TF1 = 1;						//T1定时器溢出中断标志
	TR1 = 1;						//定时器T1运行控制位
	TF0 = 1;						//T0定时器溢出中断标志
	TR0 = 1;						//定时器T0运行控制位
	
	IT0 = 1;						//外部中断T0低电平触发
	IT1 = 1;						//外部中断T1低电平触发
	
	ET1 = 1;						//定时器T1溢出中断允许位
	EX1 = 1;						//外部中断1中断允许位
	PX1 = 1;						//外部中断1中断优先级配置	
	ET0 = 1;						//定时器T0溢出中断允许位
	EX0 = 1;						//外部中断0中断允许位
	PX0 = 1;						//外部中断1中断优先级配置	
}

void uart_init()//初始化串口
{
	SCON = 0x50;	//8位UART,波特率可变
	PCON = 0x80;	//倍频
	
	TMOD |= 0x20;	//8位自动重载定时器,TH1的值自动装入TL1中
	TH1 = 0xFA;		//波特率设置:9600波特率
	TR1 = 1;			//定时器1运行控制位
	
	TMOD |= 0x01;
}

//void t0()interrupt 1//定时器T0中断
//{

//}

void display(unsigned int n)//显示函数
{
	
	unsigned char temp[5] = {0};
	puts("自动模式:");
	crlf();
	temp_read();			//读取温度
	temp[0] = temp_value/100 + '0';//蓝牙发送温度至手机
	temp[1] = temp_value%100/10 + '0';//+‘0’的含义是将数据转化为字符型
	temp[2] = '.';
	temp[3] = temp_value%10 + '0';
	puts("温度:");
	puts(temp);
	crlf();
	puts("距离:");//蓝牙发送距离至手机
	putchar(n/100+48);
	putchar(n%100/10+48);
	putchar(n%10+48);
	puts("厘米");
	crlf();
	if(temp_value > 240 && distance < 100)//进行判断是否打开风扇
	{
		IN3 = 0;
		IN4 = 1;
		puts("风扇开启");
	}
	else
	{
		IN3 = 1;
		IN4 = 1;
		puts("风扇关闭");
	}
	crlf();
	delay(1000);
}

void ex0() interrupt 0		//外部中断0
{
	mark = !mark;						//自动模式标志位取反
}

void ex1() interrupt 2		//外部中断1
{
	flag = !flag;						//手动模式标志位取反
}

void mode()								//模式切换函数
{
	if(!mark)								//自动模式启动标志
	{
		distance = 0;
		TH0 = 0;
		TL0 = 0;
		trig = 1;
		delayus(15);	//大于10us即可
		trig = 0;
		while(!echo);	//等待探头发出信号
		TR0 = 1;
		while(echo);	//接收数据
		TR0 = 0;
		distance = TH0*256+TL0;
		distance = 12/11.0592*distance*0.017;
/*************************************************
		距离=时间*速度(时间=(TH0*256+TL0)*10^(-6)*12*10^(-6)/11.0592*10^(6)/2)(速度=340)*100
		TH0*256+TL0为定时器计时时间单位为us所以需要*10^(-6),又由于晶振为11.0592的,所以对应的单位时间应该为12*10^(-6)/11.0592*10^(6)
		由于声音传出去还需要传回来所以相当于走了2倍路程需要/2
		声速约为340m/s
		但最终算出来的距离单位为  米  所以转化为厘米需要*100
		最终简化公式即为       distance = 12/11.0592*(TH0*256+TL0)*0.017;
**************************************************/
		LCD_ShowString(1,0,"TEMPERATURE:");
		LCD_ShowNum(1,13,temp_value/10,2);//LCD1602显示温度
		LCD_ShowString(1,15,".");
		LCD_ShowNum(1,16,temp_value%10,1);
		LCD_ShowString(2,0,"DISTANCE:");
		LCD_ShowNum(2,13,distance,4);		//LCD显示距离
		display(distance);			//传入计算出的距离
		delay(10000);
	}
	else									//自动模式关闭标志
	{
		
		delay(1000);
		if(!flag)						//手动模式关闭
		{
			IN3 = 1;
			IN4 = 1;
			puts("手动模式:");
			puts("风扇关闭");
			crlf();
			delay(1000);
			delay(1000);
			delay(1000);
		}
		else								//手动模式启动
		{	
			IN3 = 0;
			IN4 = 1;
			puts("手动模式:");
			puts("风扇启动");
			crlf();
			puts("温馨提示:天冷少开风扇喔~  ^.^");
			crlf();
			delay(1000);
			delay(1000);
			delay(1000);
		}
	}
}

void main()
{
	LCD_Init();			//LCD1602
	T0_init();			//定时器T0
	uart_init();		//串口
	while(1)
	{
		mode();
	}
}

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值