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();
}
}