关于串口通讯的文章:https://blog.csdn.net/jx_lihuifu/article/details/80308364
关于蓝牙模块设置的文章:https://blog.csdn.net/qimi923511491/article/details/82928743
本系统本两个部分,一个部分是采集端,一个是接收端。采集端由温度传感器DS18B20、主蓝牙ATK-HC05(蓝牙2.0)、光敏电阻及一个51单片机小系统组成。接收端由一个从蓝牙模块、报警模块、温度设定与显示模块组成。
单片机串口通讯的工作原理及蓝牙通讯协议。
单片机串口通信原理
DS18B20单总线协议
典型的单总线命令序列如下 :
第一步:初始化
第二步:ROM命令(跟随需要交换的数据 )
第三步:功能命令(跟随需要交换的数据)**
读或者写命令
DS18B20温度传感器只有三个引脚,只需要分别接电源、地及中间的引脚接到单片机的普通I/O口既可以与单片机进行通信。DS18B20是单总线通信方式,只需要一条总线就可以与单片机实现双向通信,可读可写,而且支持多点组网方式,可以实现多点测温,接线简单,使用方便。DS18B20引脚图如下图3-5所示。
DS18B20单总线协议虽然接线简单,但是对于程序部分的时隙要求非常高,DS18B20有严格的时序通讯协议,下图3-6是DS18B20的初始化时序图。
在主机初始化过程,主机(即通过单片机操作)拉低单总线至少 480us, 以产生(Tx) 复位脉冲。接着,主机释放总线,并进入接收模式(Rx)。 当总线被释放后,5k上拉电阻将单总线拉高。在单总线器件检测到上升沿后,延时 15-60us,接着从机(也就是传感器自己)通过拉低总线 60-240 us, 以产生应答脉冲 ,所以在初始化过程中我们需要做的就是拉低总线480us以上。
*******************************************************************************
* 函数名 : Ds18b20Init
* 函数功能 : 初始化
* 输入 :无
* 输出 :初始化成功返回1,不成返回0
*******************************************************************************/
unsigned char Ds18b20Init()
{
unsigned int i;
DSPORT=0; //将总线拉低480-960us
i=70;
while(i--);//延时±642us
DSPORT=1; //然后拉高总线·如果DS18B20做出反应会在15US-60Us后将总线拉低。
i=0;
while(DSPORT) //等待拉低总线,总线拉低了这个条件就不符合即跳出了
{
i++;
if(i>5000)//等待>5MS
return 0;//初始化失败
return 1;//初始化成功
转换命令
*******************************************************************************
* 函数名 : Ds18b20ChangTemp
* 功能 : 开始转换温度
* 输入 : com
* 输出 :无
*******************************************************************************/
void Ds18b20ChangTemp()
{
Ds18b20Init();
Delay1ms(1);
Ds18b20WriteByte(0xcc); //跳过ROM命令 //只有一个期间在线上的时候才能用,不然出错乱
Ds18b20WriteByte(0x44); //温度转换命令
}
/*******************************************************************************
* 函数名 : Ds18b20WriteByte
* 功能 : 写入一个字节
* 输入 : dat:一些命令入0xcc跳过ROM命令,0x44温度转换命令,0Xbe发送读取温度命令
* 输出 :无
*******************************************************************************/
void Ds18b20WriteByte(unsigned char dat)
{
unsigned int i,j;
for(j=0;j<8;j++)
{
DSPORT=0; //每写入一位数据之前都要把总线拉低1us
i++;
DSPORT=dat&0x01; //从最低位开始一为一位写入数据
i=6;
while(i--); //延迟至少60us
DSPORT=1; //释放总线接着写入第二位数
dat>>=1;
}
}
读取温度
/*******************************************************************************
* 函数名 : Ds18b20ReadTemp
* 功能 : 读取温度
* 输入 :无
* 输出 :返回16位的温度值
*******************************************************************************/
int Ds18b20ReadTemp()
{
int temp=0;
unsigned char tmh,tml;
Ds18b20ChangTemp(); //温度转换命令
Ds18b20ReadTempCom(); //温度读取命令
tml=Ds18b20ReadByte(); //开始读,一共16位,先读低字节。
tmh=Ds18b20ReadByte(); //再读高字节
temp=tmh;
temp<<=8;
temp|=tml;//把两个字节连接起来,一共16位
return temp;//返回这16位的温度数值
}
至于为什么是16位呢?前5位是符号位,后11位才是数值,其中前7位是整数位,后4位是小数位。
**所以DS18B20的温度读取到此结束:总结如下,初始化–ROM命令–功能命令。
因为是单总线通信所以时序很重要,首先是要初始化,拉低总线DSPORT=0 480us以上,再释放15us左右DS18B20做出反应自动拉低总线60-240us这时候就判断是不是变低了即可,变低即成功初始化。然后ROM命令单点总线选跳过的命令,下达温度转换命令,然后就开始读温度的命令,先读低字节再读高字节,总共两个字节16位。**这16位是二进制温度数,需要编程10进制的实际温度值乘以0.0625即可变成实际温度值+0.5四舍五入。这样子再显示出来就可以了。
1.波特率
每秒传送多少个二进制位数,1波特=1bit/1s (bps)
首先了解串口通信先要熟悉SCON,PCON,TMOD三个寄存器
RI:接收中断标志位,数据接收结束时,标志位会自动置1,需要通过程序将其置0,0
TI:发送中断标志位,数据发送结束时,标志位会自动置1,需要通过程序将其置0,0
RB8:存放发送数据的第9位,0
TB8:存放接收数据的第9位,0
REN:串行接收允许位,1允许串行接收,0禁止串行接收,1
SM2:多机控制位,0
SM1,SM0:串行工作方式
SOMD:波特率是否加倍选择位,0波特率不加倍,1波特率加倍
前四位为T1,后四位为T0,串口通信中用T1。
串口通信中用工作方式二减少误差。因为可以自动重装。
//串口接收端程序
#include
#include"lcd.h"
sbit LED = P1^0;
sbit buzz = P0^4;
//int a[10];
LED_Buffer[10];
void LcdDisplay(int);
void UsartConfiguration();
void delay_ms(uint x)
{
uint i,j;
for(i=x;i>0;i--)
for(j=148;j>0;j--);
}
/*******************************************************************************
* 函数名 : main
*******************************************************************************/
void main()
{
UsartConfiguration();
LcdInit();
buzz=1;
LcdWriteCom(0x83); //дµØÖ· 80±íʾ³õʼµØÖ·
LcdWriteData('C');
LcdWriteCom(0x80);
LcdWriteData('+');
LcdWriteCom(0xC0);
LcdWriteData('x');
LcdWriteData('i');
LcdWriteData('a');
LcdWriteData('n');
LcdWriteData('z');
LcdWriteData('h');
LcdWriteData('i');
LcdWriteData(':');
LcdWriteData('4');
LcdWriteData('0');
LcdWriteData('C');
while(1)
{
LcdWriteCom(0x81);
LcdWriteData(LED_Buffer[0]);//高位
LcdWriteCom(0x82);
LcdWriteData(LED_Buffer[1]);//低位
if(LED_Buffer[0]>='4')
buzz=0;
if(LED_Buffer[0]<='3')
buzz=1;
}
}
/*******************************************************************************
* 函数名 :UsartConfiguration()
* 函数功能 :串口设置
* 输入 : 无
* 输出 : 无
*******************************************************************************/
void UsartConfiguration()
{
SCON=0X50; //串口工作方式1,波特率可变
TMOD=0X20; //计数器工作方式2
PCON=0X80; //波特率加倍
TH1=0XF3; //波特率是4800,根据计算得到定时器初值
TL1=0XF3; //TH1和TL1一样当串口工作在方式1时,
//波特率=(2^SMOD/32)*(单片机时钟频率/((256-X)*12)),X是初值,初值就是0xF3.先知道波特率再算初值。
ES=1; //打开接收中断
EA=1; //开总中断
TR1=1; //开启定时器
}
/*******************************************************************************
* 函数名 :Com_Int()
* 函数功能 :设置中断服务程序
* 输入 : 无
* 输出 : 无
*******************************************************************************/
void Com_Int(void) interrupt 4//有Interrupt说明是中断服务程序
{
//中断服务程序里面写的是进入中断后干的事情
//中断函数不需要声明
static uchar i = 2; //静态变量
//EA = 0;//关闭总中断其实没有必要关这个
if(RI == 1) // RI:接收中断标志位,数据接收结束时,标志位会自动置1,需要通过程序将其置0
{//中断一次只接受一位数字
LED_Buffer[i] = SBUF;
RI = 0;
if(i==0) i = 2;
i--;
}
//EA = 1;//打开总中断
LED = ~LED;//指示灯闪
}
在这里插入代码片