51单片机LCD显示温度与串口接受温度

外接晶振为12MHz时,51单片机相关周期的具体值为:

振荡周期=1/12us;
状态周期=1/6us;
机器周期=1us;
指令周期=1~4us;

51单片机定时/计数器的工作由两个特殊功能寄存器控制。TMOD用于设置其工作方式;TCON用于控制其启动和中断申请。
1、工作方式寄存器TMOD
工作方式寄存器TMOD用于设置定时/计数器的工作方式,低四位用于T0,高四位用于T1。其格式如下:
在这里插入图片描述
GATE是门控位, GATE=0时,用于控制定时器的启动是否受外部中断源信号的影响。只要用软件使TCON中的TR0或TR1为1,就可以启动定时/计数器工作;GATA=1时,要用软件使TR0或TR1为1,同时外部中断引脚INT0/1也为高电平时,才能启动定时/计数器工作。即此时定时器的启动条件,加上了INT0/1引脚为高电平这一条件。
C/T :定时/计数模式选择位。C/T =0为定时模式;C/T =1为计数模式。
M1M0:工作方式设置位。定时/计数器有四种工作方式。
在这里插入图片描述
控制寄存器TCON
TCON的高4位用于控
制定时/计数器的启动和中断申请。其格式如下:
在这里插入图片描述
TF1(TCON.7):T1溢出中断请求标志位。T1计数溢出时由硬件自动置TF1为1。CPU响应中断后TF1由硬件自动清0。T1工作时,CPU可随时查询TF1的状态。所以,TF1可用作查询测试的标志。TF1也可以用软件置1或清0,同硬件置1或清0的效果一样。
TR1(TCON.6):T1运行控制位。TR1置1时,T1开始工作;TR1置0时,T1停止工作。TR1由软件置1或清0。所以,用软件可控制定时/计数器的启动与停止。
TF0(TCON.5):T0溢出中断请求标志位,其功能与TF1类同。
TR0(TCON.4):T0运行控制位,其功能与TR1类同。

定时/计数器的工作方式

1、方式1
方式1的计数位数是16位,由TL0作为低8位,TH0
作为高8位,组成了16位加1计数器 。计数个数与计数初值的关系为:X=2(16次方)-N

2、方式2
为自动重装初值的8位计数方式。 计数个数与计数初值的关系为:X=28-N
工作方式2特别适合于用作较精确的脉冲信号发生器。所以串口通信处用此方式。

计数器初值的计算
机器周期也就是CPU完成一个基本操作所需要的时间。
机器周期=1/单片机的时钟频率。
51单片机内部时钟频率是外部时钟的12分频。也就是说当外部晶振的频率输入到单片机里面的时候要进行12分频。比如说你用的是12MHZ的晶振,那么单片机内部的时钟频率就是12/12MHZ,当你使用12MHZ的外部晶振的时候。机器周期=1/1M=1us。
而我们定时1ms的初值是多少呢,1ms/1us=1000。也就是要计数1000个数,初值=65535-1000+1(因为实际上计数器计数到66636才溢出)=64536=FC18H

串口通信
比特率是每秒钟传输二进制代码的位数,单位是:位/秒(bps)。如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位、1个停止位、8个数据位),这时的比特率为:
10位×240个/秒 = 2400 bps

SCON 是一个特殊功能寄存器,用以设定串行口的工作方式、接收/发送控制以及设置状态标志:在这里插入图片描述
SM0和SM1为工作方式选择位,可选择四种工作方式:
在这里插入图片描述
SM2,多机通信控制位,主要用于方式2和方式3。当接收机的SM2=1时可以利用收到的RB8来控制是否激活RI(RB8=0时不激活RI,收到的信息丢弃;RB8=1时收到的数据进入SBUF,并激活RI,进而在中断服务中将数据从SBUF读走)。当SM2=0时,不论收到的RB8为0和1,均可以使收到的数据进入SBUF,并激活RI(即此时RB8不具有控制RI激活的功能)。通过控制SM2,可以实现多机通信。
在方式0时,SM2必须是0。在方式1时,如果SM2=1,则只有接收到有效停止位时,RI才置1。
REN,允许串行接收位。由软件置REN=1,则启动串行口接收数据;若软件置REN=0,则禁止接收

TI,发送中断标志位。在方式0时,当串行发送第8位数据结束时,或在其它方式,串行发送停止位的开始时,由内部硬件使TI置1,向CPU发中断申请。在中断服务程序中,必须用软件将其清0,取消此中断申请。

RI,接收中断标志位。在方式0时,当串行接收第8位数据结束时,或在其它方式,串行接收停止位的中间时,由内部硬件使RI置1,向CPU发中断申请。也必须在中断服务程序中,用软件将其清0,取消此中断申请。

PCON中只有一位SMOD与串行口工作有关 :SMOD(PCON.7) 波特率倍增位。在串行口方式1、方式2、方式3时,波特率与SMOD有关,当SMOD=1时,波特率提高一倍。复位时,SMOD=0。

80C51串行口的工作方式
这里只介绍方式1:
方式1是10位数据的异步通信口。TXD为数据发送引脚,RXD为数据接收引脚,传送一帧数据的格式如图所示。其中1位起始位,8位数据位,1位停止位。
在这里插入图片描述
在这里插入图片描述
用软件置REN为1时,接收器以所选择波特率的16倍速率采样RXD引脚电平,检测到RXD引脚输入电平发生负跳变时,则说明起始位有效,将其移入输入移位寄存器,并开始接收这一帧信息的其余位。接收过程中,数据从输入移位寄存器右边移入,起始位移至输入移位寄存器最左边时,控制电路进行最后一次移位。当RI=0,且SM2=0(或接收到的停止位为1)时,将接收到的9位数据的前8位数据装入接收SBUF,第9位(停止位)进入RB8,并置RI=1,向CPU请求中断。

方式1的波特率 =(2SMOD/32)·(T1溢出率)
T1 溢出率 = fosc /{12×[256 -(TH1)]}

在这里插入图片描述

DS18B20温度传感器原理
DS18B20温度转换规则
DS18B20的核心功能是它可以直接读出数字的温度数值。温度传感器的精度为用户可编程的9,10,11或12位,分别以0.5℃,0.25℃,0.125℃和0.0625℃增量递增。在上电状态下默认的精度为12位。

在这里插入图片描述
这是12位转化后得到的12位数据,存储在DS18B20的两个8位的RAM中,高字节的前5位是符号位,如果测得的温度大于0,这5位为‘0’,只要将测到的数值乘以0.0625即可得到实际温度;如果温度小于0,这5位为‘1’,测到的数值需要先减1再取反再乘以0.0625即可得到实际温度。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
(1).数据线拉到低电平“0”。
(2).延时480微妙(该时间的时间范围可以从480到960微妙)。
(3).数据线拉到高电平“1”。
(4).延时等待80微妙。如果初始化成功则在15到60微妙时间内产生一个由DS18B20所返回的低电平“0”.根据该状态可以来确定它的存在,但是应注意不能无限的进行等待,不然会使程序进入死循环,所以要进行超时判断。
(5).若CPU读到了数据线上的低电平“0”后,还要做延时,其延时的时间从发出的高电平算起(第(3)步的时间算起)最少要480微妙。

读时序
在这里插入图片描述
(1).将数据线拉低“0”。
(2).延时1微妙。
(3).将数据线拉高“1”,释放总线准备读数据。
(4).延时10微妙。
(5).读数据线的状态得到1个状态位,并进行数据处理。
(6).延时45微妙。
(7).重复1~7步骤,直到读完一个字节。

写时序
在这里插入图片描述
(1).数据线先置低电平“0”
(2).延时15微妙。
(3).按从低位到高位的顺序发送数据(一次只发送一位)。
(4).延时60微妙。
(5).将数据线拉到高电平。
(6).重复1~5步骤,直到发送完整的字节。
(7).最后将数据线拉高。

#include<reg51.h>	
#define LCD1602_DATAPINS P0

typedef	 unsigned int	  uint;
typedef unsigned char	 uchar;

sbit LCD1602_E=P2^7;
sbit LCD1602_RW=P2^5;
sbit LCD1602_RS=P2^6;
sbit DSPORT=P3^7;

uchar CNCHAR[6] = "摄氏度";

void LcdInit();
void LcdWriteData(uchar dat);
void LcdWriteCom(uchar com);
void LcdDisplay(int);
void UsartConfiguration();
uchar init();
void writebyte(uchar datas);
void change_temper();
void read_tempercom();
uint read_temper();
uchar readbyte();
void DelayMs(unsigned int x);

void main(void)	
{
	UsartConfiguration();
	LcdInit();			 //初始化LCD1602
	LcdWriteCom(0x88);	//写地址 80表示初始地址
	LcdWriteData('C'); 
	while(1)
	{
		LcdDisplay(read_temper());
//		Delay1ms(1000);//1s钟刷一次
	}				
}

void DelayMs(unsigned int x)   //0.14ms误差 0us
{
 unsigned char i;
  while(x--)
 {
  for (i = 0; i<13; i++)
 {}
 }
}

void LcdDisplay(int temp) 	 //lcd显示
{
    
  	unsigned char i, datas[] = {0, 0, 0, 0, 0}; //定义数组
	float tp;  
	if(temp< 0)				//当温度值为负数
  	{
	  	LcdWriteCom(0x80);		//写地址 80表示初始地址
		SBUF='-';//将接收到的数据放入到发送寄存器
		while(!TI);			         //等待发送数据完成
		TI=0;						 //清除发送完成标志位
	    LcdWriteData('-');  		//显示负
		//因为读取的温度是实际温度的补码,所以减1,再取反求出原码
		temp=temp-1;
		temp=~temp;
		tp=temp;
		temp=tp*0.0625*100+0.5;	
		//留两个小数点就*100,+0.5是四舍五入,因为C语言浮点数转换为整型的时候把小数点
		//后面的数自动去掉,不管是否大于0.5,而+0.5之后大于0.5的就是进1了,小于0.5的就
		//算由?.5,还是在小数点后面。
 
  	}
 	else
  	{			
	  	LcdWriteCom(0x80);		//写地址 80表示初始地址
	    LcdWriteData('+'); 		//显示正
		SBUF='+';//将接收到的数据放入到发送寄存器
		while(!TI);			         //等待发送数据完成
		TI=0;						 //清除发送完成标志位
		tp=temp;//因为数据处理有小数点所以将温度赋给一个浮点型变量
		//如果温度是正的那么,那么正数的原码就是补码它本身
		temp=tp*0.0625*100+0.5;	
		//留两个小数点就*100,+0.5是四舍五入,因为C语言浮点数转换为整型的时候把小数点
		//后面的数自动去掉,不管是否大于0.5,而+0.5之后大于0.5的就是进1了,小于0.5的就
		//算加上0.5,还是在小数点后面。
	}
	datas[0] = temp / 10000;
	datas[1] = temp % 10000 / 1000;
	datas[2] = temp % 1000 / 100;
	datas[3] = temp % 100 / 10;
	datas[4] = temp % 10;

	LcdWriteCom(0x82);		  //写地址 80表示初始地址
	LcdWriteData('0'+datas[0]); //百位 
	SBUF = '0'+datas[0];//将接收到的数据放入到发送寄存器
	while (!TI);			         //等待发送数据完成
	TI = 0;
	
	LcdWriteCom(0x83);		 //写地址 80表示初始地址
	LcdWriteData('0'+datas[1]); //十位
	SBUF = '0'+datas[1];//将接收到的数据放入到发送寄存器
	while (!TI);			         //等待发送数据完成
	TI = 0;

	LcdWriteCom(0x84);		//写地址 80表示初始地址
	LcdWriteData('0'+datas[2]); //个位 
	SBUF = '0'+datas[2];//将接收到的数据放入到发送寄存器
	while (!TI);			         //等待发送数据完成
	TI = 0;

	LcdWriteCom(0x85);		//写地址 80表示初始地址
	LcdWriteData('.'); 		//显示 ‘.’
	SBUF = '.';//将接收到的数据放入到发送寄存器
	while (!TI);			         //等待发送数据完成
	TI = 0;

	LcdWriteCom(0x86);		 //写地址 80表示初始地址
	LcdWriteData('0'+datas[3]); //显示小数点  
	SBUF = '0'+datas[3];//将接收到的数据放入到发送寄存器
	while (!TI);			         //等待发送数据完成
	TI = 0;

	LcdWriteCom(0x87);		 //写地址 80表示初始地址
	LcdWriteData('0'+datas[4]); //显示小数点 
	SBUF = '0'+datas[4];//将接收到的数据放入到发送寄存器
	while (!TI);			         //等待发送数据完成
	TI = 0;
	for(i=0; i<6; i++)
	{
	 	SBUF = CNCHAR[i];//将接收到的数据放入到发送寄存器
		while (!TI);			         //等待发送数据完成
		TI = 0;
	}

	 
}


void UsartConfiguration()
{
	SCON=0X50;			//设置为工作方式1
	TMOD=0X20;			//设置计数器工作方式2
	PCON=0X80;			//波特率加倍
	TH1=0XF3;				//计数器初始值设置,注意波特率是4800的
	TL1=0XF3;
//	ES=1;						//打开接收中断
//	EA=1;						//打开总中断
	TR1=1;					//打开计数器
}


/****************
温度传感器部分
****************/
uchar init()
{
	uchar i=0;
	DSPORT=0;
	i = 70;				   //将总线拉低480us~960us
	while(i--);//延时642us
	DSPORT=1;				 //然后拉高总线,如果DS18B20做出反应会将在15us~60us后总线拉低
	i=0;
	while(DSPORT)		   //等待DS18B20拉低总线
	{
		 DelayMs(1);
		 i++;		  
		 if(i>5)
		 {
		 	return 0;			 //初始化失败
		 }
	}
	return 1;
}

void writebyte(uchar datas)
{
	uchar i,j=1;
	for(i=0;i<8;i++)
	{
		DSPORT=0;		   //每写入一位数据之前先把总线拉低1us
		//j++;		  //此处延时1us,好像又影响不大
		DSPORT=datas&0x01;
		j=6;
		while(j--); //延时68us,持续时间最少60us
		DSPORT=1;		 //然后释放总线,至少1us给总线恢复时间才能接着写入第二个数值
		datas>>=1;
	}
}

uchar readbyte()
{
	uchar dat=0,temp;
	uint i ,j;
	for(i=0;i<8;i++)
	{
		DSPORT=0;	 //先将总线拉低1us
		j++;
		DSPORT=1;	//然后释放总线
		j++;	  //此处延时有变化,等待6us
		j++;
		temp=DSPORT;	 //读取数据,从最低位开始读取
		/*将byte左移一位,然后与上右移7位后的bi,注意移动之后移掉那位补0。*/
		dat=(dat>>1)|(temp<<7);
		j = 4;		//读取完之后等待48us再接着读取下一个数
		while(j--);;			 //好像没影响
	}
	return dat;		//不是temp
}

void change_temper()
{
	init();
	DelayMs(1);			   //无影响
	writebyte(0xcc);		  //跳过ROM操作命令	
	writebyte(0x44);		   //温度转换命令
}

void read_tempercom()
{
   	init();
	//DelayMs(1);
	writebyte(0xcc);
	writebyte(0xbe);		 //发送读取温度命令
}

uint read_temper()
{
	uchar tml,tmh;
	uint t=0;
	change_temper();			//先写入转换命令
	read_tempercom();			//然后等待转换完后发送读取温度命令
	tml=readbyte();				//读取温度值共16位,先读低字节
	tmh=readbyte();				//再读高字节
	t=tmh;
	t<<=8;			//移8位
	t=tml|t;

	return t;
}
/********************
 LCD液晶部分
********************/

void LcdWriteCom(uchar com)	  //写入命令
{
	LCD1602_E = 0;     //使能
	LCD1602_RS = 0;	   //选择发送命令
	LCD1602_RW = 0;	   //选择写入
	
	LCD1602_DATAPINS = com;     //放入命令
	DelayMs(1);		//等待数据稳定

	LCD1602_E = 1;	          //写入时序
	DelayMs(5);	  //保持时间
	LCD1602_E = 0;
}

void LcdWriteData(uchar dat)			//写入数据
{
	LCD1602_E = 0;	//使能清零
	LCD1602_RS = 1;	//选择输入数据
	LCD1602_RW = 0;	//选择写入

	LCD1602_DATAPINS = dat; //写入数据
	DelayMs(1);

	LCD1602_E = 1;   //写入时序
	DelayMs(5);   //保持时间
	LCD1602_E = 0;
}

void LcdInit()						  //LCD初始化子程序
{
 	LcdWriteCom(0x38);  //开显示
	LcdWriteCom(0x0c);  //开显示不显示光标
	LcdWriteCom(0x06);  //写一个指针加1
	LcdWriteCom(0x01);  //清屏
	LcdWriteCom(0x80);  //设置数据指针起点
}

LCD显示部分可以参考我一篇文章
51单片机DS1302时钟LCD1602显示

以上是我在学习过程中的一点总结,用的是普中的51单片机·。

  • 16
    点赞
  • 148
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
好的,以下是一个基于C51单片机的例子代码,可将DS18B20温度传感器获取到的温度值通过串口发送给电脑: ```C #include <STC12C5A60S2.H> //引入单片机头文件 #define uchar unsigned char #define uint unsigned int //定义数据线口 sbit DQ=P3^7; void delay(uint t) { while(t--); } void Delay1ms() { uchar i, j; i = 12; j = 169; do { while(--j); } while(--i); } void Start18B20() { DQ = 1; Delay1ms(); DQ = 0; Delay1ms(); DQ = 1; Delay1ms(); } uchar ReadOneChar() { uchar i, j; uchar dat = 0; for(i = 1; i <= 8; i++) { DQ = 0; j = *(&i); DQ = 1; Delay1ms(); if(DQ) { dat |= 0x01; } dat <<= 1; Delay1ms(); } return dat; } void WriteOneChar(uchar dat) { uchar i, j; for(i = 1; i <= 8; i++) { DQ = 0; j = dat & 0x01; dat >>= 1; *(&i) = j; DQ = j; Delay1ms(); DQ = 1; } } void Init18B20() { uchar x; DQ = 1; Delay1ms(); DQ = 0; Delay1ms(); DQ = 1; Delay1ms(); x = DQ; Delay1ms(); } void main() { uchar temperature = 0; uchar tempH, tempL; float temp; TMOD = 0x20; //定时器1,模式2 TH1 = 0xFD; //波特率9600 TL1 = 0xFD; TR1 = 1; //启动定时器1 SM0 = 0; SM1 = 1; REN = 1; //串口接收使能 EA = 1; //总中断使能 ES = 1; //串口中断使能 while(1) { Start18B20(); Init18B20(); WriteOneChar(0xCC); //忽略ROM操作,直接读取DS18B20的温度值 WriteOneChar(0x44); //DS18B20开始转换温度 Delay1ms(); Start18B20(); Init18B20(); WriteOneChar(0xCC); //忽略ROM操作,直接读取DS18B20的温度值 WriteOneChar(0xBE); //DS18B20读取温度值 tempL = ReadOneChar(); //读取温度值低8位 tempH = ReadOneChar(); //读取温度值高8位 temp = tempH; temp *= 256; temp += tempL; temp *= 0.0625; //计算温度值 temperature = temp; //将浮点温度值转换为整型 SBUF = temperature; //将温度数据通过串口发送给电脑 while(!TI); //等待发送完成 TI = 0; //清除发送完成标志位 Delay1ms(); } } void UART() interrupt 4 { if(RI) { RI = 0; //清除接收标志位 } } ``` 以上代码中,我们使用C51单片机的GPIO口读取DS18B20温度传感器的数据,并通过串口温度值发送给电脑。需要在Keil C51编译器中进行编译和烧录。在串口调试工具中,接收到的数据应该为温度值,例如:25。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值