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;
}
相关推荐
Groundwork Explorer41 分钟前
异步框架+POLL混合方案应对ESP32 MPY多任务+TCP多连接
python·单片机
d111111111d2 小时前
什么是内存对齐?在STM32上面如何通过编辑器指令来实现内存对齐。
笔记·stm32·单片机·嵌入式硬件·学习·编辑器
bai5459363 小时前
STM32 CuberIDE 中断
stm32·单片机·嵌入式硬件
小叶子来了啊3 小时前
5Arduino 程序结构
单片机·嵌入式硬件
小叶子来了啊3 小时前
1Arduino 简介
单片机·嵌入式硬件
雾岛听风眠4 小时前
电路板维修
单片机·嵌入式硬件
少一倍的优雅4 小时前
hi3863(WS63) 智能小车 (一) 简单介绍
单片机·嵌入式硬件·harmonyos·hi3863
小幽余生不加糖5 小时前
步进电机、有刷直流电机以及无刷直流电机对比
笔记·单片机·嵌入式硬件·学习·能源
x976665 小时前
使用 HMAC-SHA256算法对MCU UID进行加密
单片机·嵌入式硬件·算法
小叶子来了啊5 小时前
4Arduino 第一个程序
单片机·嵌入式硬件