c51串口通信原理及实操

UART(外设)

(通信主机)之间进行通信?

GND:

并行通信:一次性将多条数据传输

优点:效率高

缺点:单片机引脚资源有限,并行通信方法占用大量的应用层资源

解决方法:按位序依次发送部分数据

串行通信:同一时刻传输一个bit

例:USB

串口通信既可以是全双工的,也可以是半双工或单工的,这取决于具体的硬件设计和通信协议。

  • 全双工:最常见的串口(如RS-232、RS-422)支持全双工通信,即通信双方可以同时发送和接收数据。这是因为它们使用独立的发送线(TX)和接收线(RX),两条路信号传输互不干扰。例如,计算机与外部设备通过RS-232串口通信时,双方可同时收发数据。

  • 半双工:某些串口设计(如RS-485)默认工作在半双工模式,发送和接收共用同一组线路,因此同一时间只能单向传输(要么发送,要么接收),需要通过控制信号切换方向。

  • 单工:少数特殊场景下(如某些简单传感器的数据发送),串口可能被设计为单工模式,只能单向传输(要么只能发送,要么只能接收)。

半双工通信:

单工通信:

串口通信:

串口通信是一种特殊的串行通信,属于全双工通信

接收信号:RXD

发送信号:TXD

在串口通信中,RXD和TXD是最核心的两根数据线,用于明确数据的传输方向,具体含义如下:

1. RXD(Receive Data):接收信号线

  • 功能:用于接收外部设备发送过来的数据。
  • 例:当计算机与单片机通信时,计算机的RXD线连接单片机的TXD线,以接收单片机发送的数据;反之,单片机的RXD线连接计算机的TXD线,以接收计算机发送的指令。

2. TXD(Transmit Data):发送信号线

  • 功能:用于向外部设备发送数据。
  • 例:单片机通过自身的TXD线,将采集到的传感器数据发送到计算机的RXD线;计算机也通过自身的TXD线,将控制指令发送到单片机的RXD线。

&


空闲位:高电平

起始位:低电平

发送顺序:低位先行

奇偶校验正确率:50%

奇校验:

偶校验:

无校验:

停止位:高电平

波特率bps:1200,2400,4800,9600,115200...(bit/s)

传输1bit所需时间:1/波特率

根据所给参数计算每秒传输有效字节数:

  1. 波特率定义:9600波特率表示每秒传输9600个二进制位(bit)。

  2. 串口数据帧结构(按你的参数):

    • 起始位:1bit(固定,用于标识数据开始)
    • 数据位:8bit(约定的传输比特数)
    • 校验位:0bit(n:无奇偶校验)
    • 停止位:1bit(约定的停止位长度)
    • 每帧总长度:1+8+0+1 = 10bit
  3. 每秒传输字节数计算

    • 1字节(Byte)= 8bit(数据位,不包含控制位)
    • 每秒可传输的帧数:9600bit ÷ 10bit/帧 = 960帧
    • 每帧包含1个有效字节(8bit数据位),因此每秒传输字节数 = 960字节

关键说明

  • 停止位"不固定"是指可设置为1bit、1.5bit或2bit(根据协议约定),若你的场景中停止位为1bit,计算完全成立。
  • 若停止位为2bit,结果则为9600÷(1+8+0+2)=872字节/秒,需根据实际配置调整。

综上,在"9600波特率、8数据位、无校验、1停止位"的配置下,每秒可传输960字节


1. 同步通信与异步通信的核心区别(以SCL为标志)

  • 同步通信

    通信双方共用一条独立的时钟信号线(如I2C的SCL),发送方通过SCL线向接收方发送时钟信号,双方以该时钟的节拍同步数据传输(即"时钟同步")。

    例如:I2C、SPI协议,均通过SCL(或SCK)时钟线实现同步,数据在时钟的上升沿/下降沿被采样。

  • 异步通信

    没有独立的时钟信号线(无SCL),通信双方需提前约定相同的波特率(数据传输速率),通过数据信号中的起始位、停止位等"约定规则"实现同步。

    例如:串口通信(UART),发送方和接收方靠预设的波特率(如9600、115200)保持节奏一致,无需额外时钟线。

2. 串口通信的本质:异步串行全双工

  • 异步:如上述,串口通信没有SCL时钟线,依赖波特率约定和数据帧格式(起始位、数据位、校验位、停止位)实现同步,属于异步通信。
  • 串行:数据按位依次传输(而非并行传输多位),仅通过一根数据线(如TX/RX)即可完成单向数据传输。
  • 全双工:标准串口(如RS-232)通过两根独立的数据线(TX发送线、RX接收线)实现双向通信,双方可同时发送和接收数据(如计算机与串口设备通信时,可一边发送指令,一边接收反馈)。

3. 对比I2C(含SCL/SDA)与串口通信

协议 时钟线(SCL) 数据线(SDA/TX/RX) 通信方式 典型应用场景
I2C 有(必需) 1根(双向复用) 同步串行半双工 芯片间短距离通信(如传感器与MCU)
串口(UART) 2根(TX发送、RX接收) 异步串行全双工 设备间远距离通信(如PLC与上位机)

简言之,SCL的有无是区分同步与异步通信的关键标志:有SCL(如I2C)为同步通信,无SCL(如串口)为异步通信 。而串口通信凭借独立的收发线,同时具备了"全双工"能力,是工业控制、设备互联中最常用的异步串行通信方式。

以下是关于主机间通信及相关标准的清晰总结:

一、通信中的基础问题

单片机:使用TTL

  • 核心问题:导线存在内阻,导致电压随传输距离增加而衰减,同时易产生串扰(信号相互干扰失真)。
  • TTL电平限制
    • 电压值与芯片相关(如51单片机为5V,2440为3.3V)。
    • 5V TTL通信距离通常仅10-20米,超过则信号不稳定。

二、RS232标准(解决短距离通信)

  • 电平定义
    • 逻辑高电平:-3V ~ -15V
    • 逻辑低电平:+3V ~ +15V
  • 线路结构:收线(RX)、发线(TX)、地线(GND),共3根线。
  • 通信方式:全双工(双方可同时收发数据)。
  • 传输距离:理论20-30米。

三、RS485标准(解决长距离通信)

  • 信号传输 :通过A、B两根信号线,以两者的电压差识别信息:
    • 正电压差(A > B):高电平
    • 负电压差(A < B):低电平
    • 电压范围:±7V ~ ±12V
    • +7+12v:表1;-7-12v表0;
  • 抗干扰能力:差分信号设计,抗干扰性强。
  • 通信方式:半双工(同一时间只能单向传输)。
  • 传输距离:可达1200米,适合大范围数据传输。

三种方案对比:

类型 通信方式 传输距离 抗干扰性 适用场景
TTL 全双工 10-20米 较弱 短距离板间通信
RS232 全双工 20-30米 中等 短距离设备连接
RS485 半双工 可达1200米 长距离总线通信

实操:

需要使用到定时器1

设置定时计数器1初值:

设置为8位自动重载模式

定时器1初值计算:

SMOD:0/1

focs:晶振频率: 单位MHZ

256-21*晶振频率(12)*106/32/设定的波特率(1200)/12=204;


怎么计算不同串行口工作方式下的波特率?

TB8:奇偶校验

TI:手动清零,发送中断请求标志位,用于中断其他请求待信息发送完毕后清零

RI:手动清零,软件复位

总结:要做到发送数据:

1:打开中断开关(定时器一)

2:设置定时器一的工作模式

3:确定SCON下串行口的工作方式

4:修改SCON下的各位置一/零情况

收发数据寄存器:

写发送代码:

1200 n,8,1

1:确认工作方式:

8位:数据位比特数

9位:数据位比特数加上一比特的的奇偶校验位

所以我们选择方式一

为避免电路故障:

c 复制代码
//1:初始化函数
void init_uart(void)
{
	unsigned char t;
	t=SCON;
	t &=~(3<<6);//先将第SM0和SM1清零
	t |=(1<<6);//使用方式一:SM1置1
	SCON=t;

	
}

REN置1,

c 复制代码
void init_uart(void)
{
	unsigned char t;
	t=SCON;
	t &=~(3<<6);//先将第SM0和SM1清零
	t |=(1<<6)|(1<<4);//使用方式一:SM1置1
	SCON=t;

	
}

让SMOD置1,波特率加倍:

c 复制代码
void init_uart(void)
{
	unsigned char t;
	t=SCON;
	t &=~(3<<6);//先将第SM0和SM1清零
	t |=(1<<6)|(1<<4);//使用方式一:SM1置1
	SCON=t;
	PCON |=(1<<7);//SMOD置1
	
	//设置定时器一:

	
}

设置定时器一:

使用单片机晶振为11.0596MHZ

c 复制代码
//1:初始化函数
void init_uart(void)
{
	//设置串口:
	unsigned char t;
	t=SCON;
	t &=~(3<<6);//先将第SM0和SM1清零
	t |=(1<<6)|(1<<4);//使用方式一:SM1置1
	SCON=t;

	PCON |=(1<<7);//SMOD置1
	//设置定时器一:
	t=TMOD;
	t&= ~(3<<4);
 	t |= (2<<4);
 	t &= ~(3 << 6);
	TMOD = t;
	TH1 = 208;
	TL1 = 208;
	TCON |= (1 << 6);

	
}

发送数据:


c 复制代码
void send_char(char ch)
{
	SBUF = ch;
	while((SCON & (1 << 1)) == 0);	 //TI为1时数据发送完毕,TI为0时数据正在发送
	SCON &= ~(1 << 1);//手动置0
}

总代码:发送字符

c 复制代码
#include<reg52.h>
#include"delay.h"
//1:初始化函数
void init_uart(void)
{
	//设置串口:
	unsigned char t;
	t=SCON;
	t &=~(3<<6);//先将第SM0和SM1清零
	t |=(1<<6)|(1<<4);//使用方式一:SM1置1
	SCON=t;

	PCON |=(1<<7);//SMOD置1
	//设置定时器一:
	t=TMOD;
	t &= ~(3<<4);
 	t |= (2<<4);
    t &= ~(3<< 6);
	TMOD = t;
	TH1 = 208;	//256-2*11.0596*1000000/32/1200/12=208
	TL1 = 208;
	TCON |= (1 << 6);	//	打开定时器t1的运行控制位,置1
	
}

void send_char(char ch)
{
	SBUF = ch;
	while((SCON & (1 << 1)) == 0);	 //TI为1时数据发送完毕,TI为0时数据正在发送
	SCON &= ~(1 << 1);		//手动置0
}

int main(void)
{
	init_uart();
	while(1)
	{
		send_char('A');//65
		delay(0x9FFF);	
	}

	return 0;
}

发送字符串:(不能使用printf,但是可以使用sprintf)

记得包string.h和strlen.h库

c 复制代码
#include<reg52.h>
#include<stdio.h>
#include<string.h>
#include"delay.h"
//1:初始化函数
void init_uart(void)
{
	//设置串口:
	unsigned char t;
	t=SCON;
	t &=~(3<<6);//先将第SM0和SM1清零
	t |=(1<<6)|(1<<4);//使用方式一:SM1置1
	SCON=t;

	PCON |=(1<<7);//SMOD置1
	//设置定时器一:
	t=TMOD;
	t &= ~(3<<4);
 	t |= (2<<4);
    t &= ~(3<< 6);
	TMOD = t;
	TH1 = 208;	//256-2*11.0596*1000000/32/1200/12=208
	TL1 = 208;
	TCON |= (1 << 6);	//	打开定时器t1的运行控制位,置1
	
}

void send_char(char ch)
{
	SBUF = ch;
	while((SCON & (1 << 1)) == 0);	 //TI为1时数据发送完毕,TI为0时数据正在发送
	SCON &= ~(1 << 1);		//手动置0
}
void send_buff(const char *p,int len)
{
	while(len--)
	{
		send_char(*p++);
	}
}
int main(void)
{
	const char *s="HELLO WORLD!";
	int n=10,m=20;
	xdata char buffer[32];

	init_uart();

	while(1)
	{
	//	send_char('A');//65
		sprintf(buffer,"%d+%d=%d我去",m,n,m+n);
		send_buff(buffer,strlen(buffer));
		delay(0x9FFF);	
	}

	return 0;
}

51单片机:大端字节序

接收数据:

复制代码
IE |= (1<<7)|(1<<4);  //打开允许中断寄存器

void uart_handler(void) interrupt 4
{
	if((SCON & (1<<0)) != 0)
	{
		P2 = SBUF;
		SCON &= ~ (1<<0);//手动(软件)置0
	}
}
c 复制代码
#include<reg52.h>
#include<stdio.h>
#include<string.h>
#include"delay.h"
//1:初始化函数
void init_uart(void)
{
	//设置串口:
	unsigned char t;
	t=SCON;
	t &=~(3<<6);//先将第SM0和SM1清零
	t |=(1<<6)|(1<<4);//使用方式一:SM1置1
	SCON=t;

	PCON |=(1<<7);//SMOD置1
	
	IE |= (1<<7)|(1<<4);  //打开允许中断寄存器

	//设置定时器一:
	t=TMOD;
	t &= ~(3<<4);
 	t |= (2<<4);
    t &= ~(3<< 6);
	TMOD = t;
	TH1 = 208;	//256-2*11.0596*1000000/32/1200/12=208
	TL1 = 208;
	TCON |= (1 << 6);	//	打开定时器t1的运行控制位,置1
	
}

void uart_handler(void) interrupt 4
{
	if((SCON & (1<<0)) != 0)
	{
		P2 = SBUF;
		SCON &= ~ (1<<0);//手动(软件)置0
	}
}



void send_char(char ch)
{
	SBUF = ch;
	while((SCON & (1 << 1)) == 0);	 //TI为1时数据发送完毕,TI为0时数据正在发送
	SCON &= ~(1 << 1);		//手动置0
}
void send_buff(const char *p,int len)
{
	while(len--)
	{
		send_char(*p++);
	}
}
int main(void)
{
	int a,b,c,d,f,g;
	const char *s="HELLO WORLD!";
	int n=10,m=20;

	xdata char buffer[32];
	a=sizeof(int);	//2
	b=sizeof(char);	 //1
	c=sizeof(short); //2
	d=sizeof(long);	//4
//	e=sizeof(longlong);//
	f=sizeof(float); //	4
	g=sizeof(double);//	4
	

	init_uart();

	while(1)
	{
		
	//	send_char('A');//65
		sprintf(buffer,"size=%d\n",g);
		send_buff(buffer,strlen(buffer));
		delay(0x9FFF);	
	}

	return 0;
}

主从应答:

c 复制代码
#include<reg52.h>
#include<stdio.h>
#include<string.h>
#include"delay.h"
//1:初始化函数
void init_uart(void)
{
	//设置串口:
	unsigned char t;
	t=SCON;
	t &=~(3<<6);//先将第SM0和SM1清零
	t |=(1<<6)|(1<<4);//使用方式一:SM1置1
	SCON=t;

	PCON |=(1<<7);//SMOD置1
	
	IE |= (1<<7)|(1<<4);  //打开允许中断寄存器

	//设置定时器一:
	t=TMOD;
	t &= ~(3<<4);
 	t |= (2<<4);
    t &= ~(3<< 6);
	TMOD = t;
	TH1 = 208;	//256-2*11.0596*1000000/32/1200/12=208
	TL1 = 208;
	TCON |= (1 << 6);	//	打开定时器t1的运行控制位,置1
	
}

//定义片外缓冲区
xdata char rcv_buffer[64];
int pos = 0;//接收字符个数



void uart_handler(void) interrupt 4
{
	if((SCON & (1<<0)) != 0)
	{
		rcv_buffer[pos++] = SBUF;
		SCON &= ~ (1<<0);
	}
}



void send_char(char ch)
{
	SBUF = ch;
	while((SCON & (1 << 1)) == 0);	 //TI为1时数据发送完毕,TI为0时数据正在发送
	SCON &= ~(1 << 1);		//手动置0
}
void send_buff(const char *p,int len)
{
	while(len--)
	{
		send_char(*p++);
	}
}
int main(void)
{
	
	

	init_uart();

	while(1)
	{
		
	//	send_char('A');//65
	if(pos !=0)
	{
		delay(0xFFFF);
		send_buff(rcv_buffer,pos);
	}
	}
	return 0;
}

规定应答:

复制代码
xdata char rcv_buffer[64]={0};//初始化

memset(rcv_buffer,0,sizeof(rcv_buffer));//记得清空缓冲区!!!
c 复制代码
#include<reg52.h>
#include<stdio.h>
#include<string.h>
#include"delay.h"
//1:初始化函数
void init_uart(void)
{
	//设置串口:
	unsigned char t;
	t=SCON;
	t &=~(3<<6);//先将第SM0和SM1清零
	t |=(1<<6)|(1<<4);//使用方式一:SM1置1
	SCON=t;

	PCON |=(1<<7);//SMOD置1
	
	IE |= (1<<7)|(1<<4);  //打开允许中断寄存器

	//设置定时器一:
	t=TMOD;
	t &= ~(3<<4);
 	t |= (2<<4);
    t &= ~(3<< 6);
	TMOD = t;
	TH1 = 208;	//256-2*11.0596*1000000/32/1200/12=208
	TL1 = 208;
	TCON |= (1 << 6);	//	打开定时器t1的运行控制位,置1
	
}

//定义片外缓冲区
xdata char rcv_buffer[64]={0};
int pos = 0;//接收字符个数



void uart_handler(void) interrupt 4
{
	if((SCON & (1<<0)) != 0)
	{
		rcv_buffer[pos++] = SBUF;
		SCON &= ~ (1<<0);
	}
}



void send_char(char ch)
{
	SBUF = ch;
	while((SCON & (1 << 1)) == 0);	 //TI为1时数据发送完毕,TI为0时数据正在发送
	SCON &= ~(1 << 1);		//手动置0
}
void send_buff(const char *p,int len)
{
	while(len--)
	{
		send_char(*p++);
	}
}
int main(void)
{
	
	

	init_uart();

	while(1)
	{
		
	//	send_char('A');//65
		if(pos !=0)
		{
			delay(0xFFFF);
			if(strcmp(rcv_buffer,"china")==0)
			{
				send_buff("ok",2);
			}
			else if(strcmp(rcv_buffer,"hello")==0)
			{
				send_buff("confirm",7);
			}
			pos = 0;
			memset(rcv_buffer,0,sizeof(rcv_buffer));//记得清空缓冲区!!!
			
		}
		}
	return 0;
}

传递数据时常用hex:十六进制数传递

modbus协议:

通常上位机一个,下位机若干个

上位机生成协议数据:

下位机读取执行

执行完毕后下位机回复数据给上位机:

注意功能码最高位的0转为1!!

关于DS18B20的概念

释放总线

上拉电阻:VCC

上拉电阻作用:双方在释放总线时总线能够达到高电平

下拉电阻:GND

1. 释放总线

总线是多个设备(如芯片、模块)共享的通信线路,同一时间通常只允许一个设备"占用"总线发送信号,其他设备需处于"接收"或"空闲"状态。

"释放总线"指的是:当一个设备完成数据发送后,停止对总线的驱动(不再主动输出高电平或低电平),让总线恢复到"空闲状态",以便其他设备可以占用总线进行通信。

简单说,就是设备"放手"总线,不再控制它的电平状态。

2. 上拉电阻:VCC

上拉电阻是一端连接总线,另一端连接电源(VCC,高电平)的电阻。

  • 连接方式:总线 ←→ 上拉电阻 ←→ VCC(电源正极)。
  • 作用核心:当所有设备都"释放总线"(不再驱动总线)时,上拉电阻会将总线"拉到"高电平(因为电阻导通微弱电流,总线电平接近VCC),让总线保持一个稳定的空闲状态。

3. 上拉电阻作用:双方在释放总线时总线能够达到高电平

这是上拉电阻的核心功能之一。在多设备共享总线的场景中(比如两个设备A和B通信):

  • 当A发送完数据、释放总线,且B也未发送数据(同样释放总线)时,总线本身没有设备驱动,理论上电平可能不稳定(受干扰、寄生电容等影响)。
  • 上拉电阻此时会通过微弱电流将总线"拉向"VCC,确保总线在空闲时稳定为高电平,避免状态混乱,为下一次通信提供明确的初始状态。

4. 下拉电阻:GND

下拉电阻是一端连接总线,另一端连接地(GND,低电平)的电阻。

  • 连接方式:总线 ←→ 下拉电阻 ←→ GND(电源负极)。
  • 作用核心:与上拉电阻相反,当所有设备释放总线时,下拉电阻会将总线"拉到"低电平,确保总线空闲时稳定为低电平。

上拉电阻或下拉电阻的阻值越小,对总线的影响主要体现在以下几个方面:

  1. 增大静态功耗

    电阻越小,根据欧姆定律(I=U/R),当总线被拉到高电平(上拉)或低电平(下拉)时,流过电阻的电流越大,导致电路的静态功耗增加,尤其在总线长期处于稳定状态时,这种功耗浪费更为明显。

  2. 增强总线驱动能力(抗干扰)
    小阻值电阻能提供更强的拉电流(上拉)或灌电流(下拉)能力,当总线上存在外界干扰(如噪声、瞬间低电平/高电平信号)时,更能快速将总线拉回稳定的高/低电平,减少干扰对总线状态的影响,提高总线的抗干扰能力。

  3. 延长总线状态切换时间

    总线上通常存在分布电容(导线、器件引脚等形成的寄生电容),电阻越小,RC时间常数(τ=R×C)越小,理论上状态切换更快?这里可能存在误解:实际中,当外部器件驱动总线改变状态时(如从高电平拉到低电平),小阻值上拉电阻会与驱动器件形成竞争------驱动器件需要提供更大的电流来克服上拉电阻的拉电流,反而可能延缓总线从高到低的切换速度;同理,下拉电阻过小时,也可能延缓总线从低到高的切换。因此,过小的电阻可能增加状态切换的延迟。

  4. 可能损坏驱动器件

    若上拉/下拉电阻过小,当外部驱动器件试图将总线拉到与电阻相反的电平(如上拉电阻接VCC,驱动器件想将总线拉到低电平)时,会形成较大的电流回路(VCC→上拉电阻→驱动器件→GND),过大的电流可能超过驱动器件的最大允许电流,导致器件损坏。

综上,上拉/下拉电阻的阻值需根据总线速度、功耗要求、驱动能力及抗干扰需求综合选择,并非越小越好。

上拉电阻:4.7~10k

DS18B20接VCC,电阻为上拉电阻

1:判断DS18B20温度传感器是否存在并复位该传感器:

这是单总线(1 - WIRE BUS)通信中 DS1820 器件的初始化流程,核心是"复位脉冲(Reset Pulse)"和"存在脉冲(Presence Pulse)"的交互,用于主设备(Master)与 DS1820 建立通信前的"握手"。

1. 流程阶段与时间参数

  • 主设备发送复位脉冲(Master Tx "reset pulse")
    主设备主动将总线拉低(Bus master active low,黑色实线)【通过】,持续时间最小 480μs,最大 960μs。目的是"复位"总线上的 DS1820,让器件进入待响应状态。

  • DS1820 等待与发送存在脉冲

    主设备释放总线后,总线通过**上拉电阻(Resistor pull - up,黑色细实线)**回到高电平(接近 VCC)。

    DS1820 检测到总线由低变高后,等待 15 - 60μs,然后主动将总线拉低(DS1820 active low,灰色实线),发送"存在脉冲",持续时间 60 - 240μs。这是 DS1820 向主设备"报告自己存在"的信号。

  • 主设备接收存在脉冲(Master Rx)

    主设备需在释放总线后,至少等待 480μs 来检测 DS1820 的存在脉冲,确认 DS1820 已准备好通信。

2. 线型图例(LINE TYPE LEGEND)

  • Bus master active low(黑色实线):主设备主动将总线拉低的阶段。
  • DS1820 active low(灰色实线):DS1820 主动将总线拉低的阶段(存在脉冲)。
  • Resistor pull - up(黑色细实线):总线无设备主动拉低时,由上拉电阻拉至高电平的阶段。

3. 核心逻辑

单总线是"一主多从"的通信方式(一条线同时传数据和供电),初始化的"复位 - 存在脉冲"是为了:

  • 主设备通过复位脉冲"唤醒/同步"从设备(DS1820);
  • DS1820 通过存在脉冲"应答"主设备,确认自身在线且可通信。

只有完成这个交互,主设备才能后续向 DS1820 发送读写命令(如温度转换、数据读取等)。

代码:

c 复制代码
#include<reg52.h>
#include<intrins.h>
#include"delay.h"

#define DS18B20_SET (P3 |= (1<<7))//将P37电平拉高
#define DS18B20_CLEAR (P3 &= ~(1<<7))//将P37电平拉低
#define DS18B20_TST ((P3 & (1<<7))!=0)//判断P37电平的电平高/低状态

// 11.0592MHz晶振版本 - 优化版
void Delay10us(unsigned int n)	//针对晶振频率为11.0592MHz的情况,函数功能:延迟n*10us
{
	unsigned char data i;

	_nop_();
	_nop_();
	_nop_();
	i = 2 * n;
	while (--i)
	{
		_nop_();
		_nop_();
	}
}

int reset_ds18b20(void)
{
    int t;
    DS18B20_CLEAR;   // 主设备拉低总线,发送复位脉冲
    Delay10us(70);   // 拉低持续约 700μs(符合"最小 480μs,最大 960μs")
    DS18B20_SET;     // 释放总线,由上拉电阻拉到高电平
    Delay10us(4);    // 等待 DS18B20 响应

    t = 0;
    // 检测 DS18B20 是否拉低总线(存在脉冲的开始)
    while(DS18B20_TST && t < 30) 
    {
        Delay10us(1);
        ++t;
    }
    if(t >= 30) 
    {    // 超时,未检测到 DS18B20 拉低总线
        return 0;
    }

    t = 0;
    // 检测 DS18B20 是否释放总线(存在脉冲的结束,总线回到高电平)
    while(!DS18B20_TST && t < 30) 
    {
        Delay10us(1);
        ++t;
    }
    if(t >= 30) 
    {    // 超时,DS18B20 未释放总线
        return 0;
    }
    return 1;        // 复位成功,检测到 DS18B20 存在
}

int main(void)
{
	if(reset_ds18b20()!=0)
	{
		P2=0;
		delay(0xFFFF);
		P2=0xFF;
	}
	while(1)
	{
		
	}
	return 0;
}

温度传感器存在并且复位的标志:LED灯亮一段时间后熄灭。

向温度传感器写入数据:

这是单总线(1 - WIRE BUS)通信中,主设备向 DS1820 写入"0"和"1"的时序图,核心是两种"写时隙(Write Slot)"的时序规范,用于主设备向 DS1820 传输二进制数据(0 或 1)。

1. 左侧:主设备写"0"时隙(MASTER WRITE "0" SLOT)

主设备向 DS1820 写"0"时,需遵循以下时序:

  • 主设备拉低总线 :主动将总线拉低(图中黑色实线部分),持续时间要求 60 μs < T_X "0" < 120 μs(即拉低时间在 60 微秒到 120 微秒之间)。
  • DS1820 采样 :在主设备拉低总线后,DS1820 会在特定窗口内采样总线电平:
    • 从拉低开始,先等待 15 μs,然后在 15 μs(典型值) 的窗口内采样(采样窗口总跨度约 15 + 30 = 45 μs)。
    • 由于此时总线被主设备持续拉低,DS1820 采样到低电平,即接收到"0"。

2. 右侧:主设备写"1"时隙(MASTER WRITE "1" SLOT)

主设备向 DS1820 写"1"时,时序与写"0"有明显区别:

  • 主设备短暂拉低后释放总线
    • 先将总线拉低,但拉低时间小于 15 μs (图中黑色短实线部分,标注 >1 μs,实际需足够短)。
    • 然后释放总线 ,总线通过上拉电阻回到高电平(图中带斜线的上升沿部分,最终接近 VCC)。
  • DS1820 采样 :DS1820 同样在拉低后的特定窗口内采样:
    • 从拉低开始,等待 15 μs 后,在 15 μs(典型值) 的窗口内采样。
    • 此时总线已被上拉电阻拉到高电平,DS1820 采样到高电平,即接收到"1"。

核心逻辑

单总线是"一主多从"的通信方式,主设备通过控制总线拉低的持续时间,让 DS1820 区分"0"和"1":

  • 写"0":持续拉低总线,让 DS1820 采样到低电平。
  • 写"1":短暂拉低后释放,让 DS1820 采样到上拉后的高电平。

这种时序设计确保了主设备能向 DS1820 可靠传输二进制数据(如配置命令、地址等)。

c 复制代码
void ds18b20_write(unsigned char n)  // 写入1字节(8位)数据
{
    int i;
    for(i = 0; i < 8; ++i)
    {
        if(n & 0x01)  // 写"1"时隙
        {
            DS18B20_CLEAR;       // 拉低总线
            Delay10us(1);        // 拉低时间约10μs(<15μs,符合写"1"要求)
            DS18B20_SET;         // 释放总线(由上拉电阻拉到高电平)
            Delay10us();        // 保持高电平至少50μs,确保整个时隙≥60μs
        }
        else  // 写"0"时隙
        {
            DS18B20_CLEAR;       // 拉低总线
            Delay10us(7);        // 拉低时间约70μs(60~120μs,符合写"0"要求)
            DS18B20_SET;         // 释放总线
            Delay10us(1);        // 短暂延时,确保时隙间隔
        }
        n >>= 1;  // 右移,处理下一位
    }
}
c 复制代码
#include<reg52.h>
#include<intrins.h>
#include"delay.h"
#include<string.h>
#include<stdio.h>
#include<stdlib.h>

#define DS18B20_SET (P3 |= (1<<7))
#define DS18B20_CLEAR (P3 &= ~(1<<7))
#define DS18B20_TST ((P3 & (1<<7))!=0)

void init_uart(void)
{
    unsigned char t;
    t = SCON;
    t &= ~(3 << 6);
    t |= (1 << 6) | (1 << 4); 
    SCON = t;

    PCON |= (1 << 7); 
    IE |= (1 << 7) | (1 << 4); 

   
    t = TMOD;
    t &= ~(3 << 4); 
    t |= (2 << 4); 
    TMOD = t;
    TH1 = 208;      
    TL1 = 208;
    TCON |= (1 << 6); 
}
// 11.0592MHz晶振版本 - 优化版
void Delay10us(unsigned int n)	//@11.0592MHz
{
	unsigned char data i;

	_nop_();
	_nop_();
	_nop_();
	i = 2 * n;
	while (--i)
	{
		_nop_();
		_nop_();
	}
}
void delay_1ms(unsigned int n)
{
	while(n--)
	{
		 Delay10us(100);
	}
}
xdata char rcv_buffer[64] = {0};
int pos = 0;

void uart_handler(void) interrupt 4
{
	 if((SCON & (1 << 0)) != 0)
	 {
	 	rcv_buffer[pos++] = SBUF;
		SCON &= ~(1 << 0);
	 }
}

void send_char(char ch)
{
	SBUF = ch;
	while((SCON & (1 << 1)) == 0);
	SCON &= ~(1 << 1);
}

void send_buffer(const char *p, int len)
{
	while(len--)
	{
		send_char(*p++);
	}	
}


int reset_ds18b20(void)
{
    int t;
    DS18B20_CLEAR;   // 主设备拉低总线,发送复位脉冲
    Delay10us(70);   // 拉低持续约 700μs(符合"最小 480μs,最大 960μs")
    DS18B20_SET;     // 释放总线,由上拉电阻拉到高电平
    Delay10us(4);    // 等待 DS18B20 响应

    t = 0;
    // 检测 DS18B20 是否拉低总线(存在脉冲的开始)
    while(DS18B20_TST && t < 30) {
        Delay10us(1);
        ++t;
    }
    if(t >= 30) {    // 超时,未检测到 DS18B20 拉低总线
        return 0;
    }

    t = 0;
    // 检测 DS18B20 是否释放总线(存在脉冲的结束,总线回到高电平)
    while(!DS18B20_TST && t < 30) {
        Delay10us(1);
        ++t;
    }
    if(t >= 30) {    // 超时,DS18B20 未释放总线
        return 0;
    }
    return 1;        // 复位成功,检测到 DS18B20 存在
}
void ds18b20_write(unsigned char n)	//0x01010 110 & 0000 0001
{
	int i;
	for(i = 0;i < 8;++i)
	{
		if(n & 0x01) // 1
		{
			DS18B20_CLEAR;
			_nop_();
			_nop_();
			_nop_();
			_nop_(); //不能同函数调用,单片机性能太弱。
			DS18B20_SET;
			Delay10us(5);				
		}
		else
		{
		 	DS18B20_CLEAR;
			Delay10us(6);
			DS18B20_SET;
		}
		n >>= 1;	
	}
}

unsigned char ds18b20_read(void)
{
	unsigned char ret = 0;
	int i;
	for(i = 0;i < 8;++i)
	{
		DS18B20_CLEAR;
		_nop_();
		_nop_();
		_nop_();
		_nop_();
		DS18B20_SET;
		_nop_();
		_nop_();
		_nop_();
		_nop_();
		_nop_();
		_nop_();
		if(DS18B20_TST)
		{
			ret |= 1 << i;	
		}
		Delay10us(5);							
	}
	return ret;
}

float get_temperatuer(void)
{
	unsigned char tl, th;
	short t;
	xdata char s[24];
	reset_ds18b20();
	ds18b20_write(0xCC);
	ds18b20_write(0x44);
	ds18b20_write(0x4E);
	delay_1ms(750);
	reset_ds18b20();
	ds18b20_write(0xCC);
	ds18b20_write(0xBE);
	delay_1ms(750);
	tl = ds18b20_read();
	th = ds18b20_read();
	
	t = tl;
	t |= th << 8;
	return t * 0.0625;
	
}


int main(void)
{
	xdata char s[24];
	init_uart();
	if(reset_ds18b20()!=0)
	{
		P2=0;
		delay(0xFFFF);
		P2=0xFF;
	}
	while(1)
	{
		float f;
		f = get_temperatuer();
		sprintf(s,"%f" , f);
		send_buffer(s , strlen(s));
		
		
	}
	return 0;
}
相关推荐
猫头虎6 小时前
2025最新超详细FreeRTOS入门教程:第一章 FreeRTOS移植到STM32
stm32·单片机·嵌入式硬件·机器人·硬件架构·freertos·嵌入式实时数据库
清风6666668 小时前
基于STM32单片机的酒驾检测设计
stm32·单片机·嵌入式硬件·毕业设计·课程设计
恒森宇电子有限公司8 小时前
IP5326_BZ 支持C同口输入输出的移动电源芯片 2.4A的充放电电流 支持4LED指示灯
c语言·开发语言·单片机
涂山苏苏⁠9 小时前
STM32之ADC
stm32·单片机·adc
曙曙学编程9 小时前
stm32——NVIC,EXIT
c语言·c++·stm32·单片机·嵌入式硬件
今日待办10 小时前
Arduino Nano33 BLESense Rev2【室内空气质量检测语音识别蓝牙调光台灯】
c语言·单片机·嵌入式硬件·mcu·语音识别·ardunio·arduinonano33
不懂机器人10 小时前
51单片机------中断系统
单片机·嵌入式硬件·51单片机
2501_9212904410 小时前
嵌入式第四十六天(51单片机(通信))
单片机·嵌入式硬件·51单片机
IT阳晨。11 小时前
【STM32】时钟树和GPIO
stm32·单片机·嵌入式硬件