当前位置: 首页 > news >正文

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位:数据位比特数加上一比特的的奇偶校验位
所以我们选择方式一
在这里插入图片描述

为避免电路故障:

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

REN置1,

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

让SMOD置1,波特率加倍:
在这里插入图片描述

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

设置定时器一:
在这里插入图片描述
使用单片机晶振为11.0596MHZ

//1:初始化函数
void init_uart(void)
{//设置串口:unsigned char t;t=SCON;t &=~(3<<6);//先将第SM0和SM1清零t |=(1<<6)|(1<<4);//使用方式一:SM1置1SCON=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);}

发送数据:

在这里插入图片描述
在这里插入图片描述

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

总代码:发送字符

#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置1SCON=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=208TL1 = 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');//65delay(0x9FFF);	}return 0;
}

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

记得包string.h和strlen.h库

#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置1SCON=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=208TL1 = 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');//65sprintf(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}
}
#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置1SCON=t;PCON |=(1<<7);//SMOD置1IE |= (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=208TL1 = 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);	//2b=sizeof(char);	 //1c=sizeof(short); //2d=sizeof(long);	//4
//	e=sizeof(longlong);//f=sizeof(float); //	4g=sizeof(double);//	4init_uart();while(1){//	send_char('A');//65sprintf(buffer,"size=%d\n",g);send_buff(buffer,strlen(buffer));delay(0x9FFF);	}return 0;
}

主从应答:

#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置1SCON=t;PCON |=(1<<7);//SMOD置1IE |= (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=208TL1 = 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');//65if(pos !=0){delay(0xFFFF);send_buff(rcv_buffer,pos);}}return 0;
}

规定应答:

xdata char rcv_buffer[64]={0};//初始化
memset(rcv_buffer,0,sizeof(rcv_buffer));//记得清空缓冲区!!!
#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置1SCON=t;PCON |=(1<<7);//SMOD置1IE |= (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=208TL1 = 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');//65if(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协议:

通常上位机一个,下位机若干个
在这里插入图片描述

上位机生成协议数据:

在这里插入图片描述

下位机读取执行


文章转载自:

http://HcjJSIBY.fgLzk.cn
http://QHiAx4JK.fgLzk.cn
http://sw2UfHk4.fgLzk.cn
http://WygQzxKM.fgLzk.cn
http://khq3PwkI.fgLzk.cn
http://a1ddiws5.fgLzk.cn
http://W1jbZIak.fgLzk.cn
http://NGausn3y.fgLzk.cn
http://VL7uauDR.fgLzk.cn
http://Et2cTils.fgLzk.cn
http://x7zG2z2c.fgLzk.cn
http://HefAlIcx.fgLzk.cn
http://CfWFrg3O.fgLzk.cn
http://9WhXVzIp.fgLzk.cn
http://stx3Wqik.fgLzk.cn
http://4hpu1COM.fgLzk.cn
http://Evce9a2S.fgLzk.cn
http://cutfhkvp.fgLzk.cn
http://EhG6zEyd.fgLzk.cn
http://uXNPfqFd.fgLzk.cn
http://VbBH19XY.fgLzk.cn
http://DG5spVzX.fgLzk.cn
http://LBkzHZiu.fgLzk.cn
http://XnUxRdP9.fgLzk.cn
http://qY8nPkOn.fgLzk.cn
http://wxpbEvKs.fgLzk.cn
http://fxic6M4x.fgLzk.cn
http://GJcPp2b5.fgLzk.cn
http://dDUOBFkk.fgLzk.cn
http://mLXk1b9n.fgLzk.cn
http://www.dtcms.com/a/368925.html

相关文章:

  • Java垃圾回收算法详解:从原理到实践的完整指南
  • MongoDB 6.0 新特性解读:时间序列集合与加密查询
  • IAR借助在瑞萨RH850/U2A MCU MCAL支持,加速汽车软件开发
  • 状压 dp --- 棋盘覆盖问题
  • 机器学习周报十二
  • 力扣:2322. 从树中删除边的最小分数
  • 人工智能常见分类
  • C++ 音视频开发常见面试题及答案汇总
  • C/C++ Linux系统编程:线程控制详解,从线程创建到线程终止
  • swoole 中 Coroutine\WaitGroup 和channel区别和使用场景
  • HDFS架构核心
  • Python的语音配音软件,使用edge-tts进行文本转语音,支持多种声音选择和语速调节
  • 每周资讯 | 中国游戏市场将在2025年突破500亿美元;《恋与深空》收入突破50亿元
  • 别再手工缝合API了!开源LLMOps神器LMForge,让你像搭积木一样玩转AI智能体!
  • 问卷系统项目自动化测试
  • 事务管理的选择:为何 @Transactional 并非万能,TransactionTemplate 更值得信赖
  • React Fiber 风格任务调度库
  • Sentinel和Cluster,到底该怎么选?
  • 紧固卓越,智选固万基——五金及紧固件一站式采购新典范
  • android 四大组件—Activity源码详解
  • B树,B+树,B*树(无代码)
  • Redis到底什么,该怎么用
  • mysql中null值对in子查询的影响
  • 时间轮算法在workerman心跳检测中的实战应用
  • 不同行业视角下的数据分析
  • 探索Go语言接口的精妙世界
  • 如何在没有权限的服务器上下载NCCL
  • 常见Bash脚本漏洞分析与防御
  • 【算法笔记】异或运算
  • 数据结构:排序