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

单片机-89C51部分:9、串行口通讯

飞书文档https://x509p6c8to.feishu.cn/wiki/WSh3wnADkixHspk7kc8c5esRnad

一、什么是串口?它的作用?

串行口,简称为串口,什么是串口?它的作用是什么?

两个人交流,一般通过在说话在空气中产生的声波传输,两台机器交流,我们可以模拟摩斯密码一样,其中一台机器通过IO发送对应频率高低电平的脉冲,另一台机器接收进行解析,但这种操作IO的方式需要我们考虑的问题很多,多快的频率,谁发谁收,丢失了一个脉冲怎么办等等,于是后面就有了规范这些交流的协议,简称为通讯协议,基于通讯方式的不同,也有了串行通讯和并行通讯两种通讯方式。

串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信。

串行通信

串行通信是指使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以在系统间交换信息,特别适用于计算机与计算机,计算机与外设之间的远距离通信,先传输低位在传输高位。如下图所示:

并行通信

并行通信通常是将数据字节的各位用多条数据线同时进行传送,通常是8位,16位,32位等数据一起传输。如下图所示:

对比:

串行通信的特点:传输线少,长距离传送时成本低,且可以利用电话网等现成的设备,但数据的传送控制比并行通信复杂。

并行通信的特点:控制简单,传输速度快;由于传输线较多,长距离传送时成本高且接收方的各位同时接受存在困难,抗干扰能力差。

现阶段绝大部分的通讯口都使用串口。

二、串口的参数

串行通信的基本方式

单工通信:数据只能单方向传输。

半双工通信:通信双方交替进行双向数据传输,但两个方向的传输不能同时进行。

全双工通信:通信双方可同时进行数据收发的工作方式。51单片机的串行口是全双工传输方式。

串口电平标准

是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:

• TTL电平(transistor transistor logic ): +3V~+5V 表示 1 , 0V 表示 0

• RS232 电平: -3~-15V 表示 1 , +3~+15V 表示 0

• RS485 电平:两线压差 +2~+6V 表示 1 , -2~-6V 表示 0 (差分信号)

串口数据结构

一个完整的串行数据,也就是一个数据帧(Data frame),包括起始位、数据位、停止位、奇偶校验位。数据位前后即帧头和帧尾,包含一些必要的控制信息。其中,MSB(Most Significant Bit)是指低地址存放最高有效字节,LSB(Least Significant Bit)则是低地址存放最低有效字节。

  1. 20 - > 0x14 -> 0b0001 0100
  2. MSB:     0010 1000
  3. LSB:      0001 0100

串口波特率

通俗解析就是,波特率越高,传输速度越快。

常见的串口典型的“波特率”值是 300/1200/2400/9600/19200/38400/115200 /230400等。

串口通信速率,单位时间内传输二进制的位数(例波特率为9600,指1s内传输9600位,则传输一位需要1/9600=104.17us)

串口校验位

N 无校验

不加校验位,可以少传输一位数据

O 奇校验

a要传输的数据中(不包含校验位)有奇数个‘1’ 则校验位为‘0’, 反之为‘1’

例: 数据‘1111 000’ 偶数个‘1’ 所以添加校验位为‘1’ 整体为‘1111 0000 1’

E  偶校验

要传输的数据中(不包含校验位)有偶数个‘1’ 则校验位为‘0’, 反之为‘1’

M(Mark 标记、符合)检验位固定为1

S(Space 空间、空地)校验位固定为0

串口停止位

停止位,停止位是一帧数据结束的标志,可以是1bit、1.5bit或者2bit逻辑“1” 高电平

如果没有停止位,接收设备就无法知道何时一个数据包结束,从而无法正确地处理接收到的数据。

空闲位

空闲位不算是串口报文内的数据, 它是发送完一组报文后,总线会自动将电平拉高,产生1bit 逻辑“1”的空闲位

串口调试助手

https://alithon.com/downloads

主要使用CH340驱动软件和逻辑分析仪驱动软件,可以点击飞书文档链接下载

USB转串口助手:

逻辑分析仪:

三、51的串口使用

硬件接线

串口相关寄存器:

一般常用的是SCON SBUF PCON IE,如果需要配置不同中断优先级的则需要配置IPH IP,SADEN、SADDR用于多机通讯,比较少用。

模式选择

STC89C52 有 1 个 UART, 有四种工作模式:

模式0:同步移位寄存器,主要用于扩展并行输入或输出口。

模式18UART,波特率可变(常用)

模式2:9位UART,波特率固定(多出的1位为校验位)

模式3:9位UART,波特率可变(多出的1位为校验位)

这里简单说明下常用和其它的区别,常用的就是你使用串口功能时,一般使用模式1就能满足所有需求,像我工作这么久,做了非常多的项目,其它模式基本上没用过,所以学习时把常用的学会,原理搞懂就可以了,后面根据项目需求有目的性学习,而不是把所有细节都学一遍。

模式选择寄存器

SM0 = 0;
SM1 = 1;


SCON =
0x40;                //0100 0000 串口工作模式1

模式1说明

发送数据相关寄存器配置

发送流程

SBUF 串口数据缓冲寄存器:可写入需要发送的数据

时钟源选择,影响波特率

Timer1 Overflow:定时器1溢出作为时钟源,用于串口通讯的波特率

查表法:

方式一:
TMOD = 0X20;   //设置定时器1工作模式2 8位自动重载
TH1 = 0xFD;    //设定初值
TL1 = 0XFD;    //设定装载值
TR1 = 1;       //打开计数器


TMOD &= 0x0F;  //清空TMOD中定时器1相关
TMOD |= 0X20;  //设置定时器1工作模式2 8位自动重载,这里为什么使用|=而不使用=呢,为了避免清除定时器0配置
TH1 = 0xFD;    //设定初值
TL1 = 0XFD;    //设定装载值
TR1 = 1;       //打开计数器

公式法:


 

如果需要设置波特率为9600=(1/32)*(11059000/12/(256-TH1))

SMOD:波特率加倍选择位,复位为0,可以不设置

方式一:
TMOD &= 0x0F;  //清空TMOD中定时器1相关
TMOD |= 0X20;  //设置定时器1工作模式2 8位自动重载,这里为什么使用|=而不使用=呢,为了避免清除定时器0配置
TH1 = 0xFD;    //设定初值
TL1 = 0XFD;    //设定装载值
TR1 = 1;       //打开计数器
PCON |= 0X00;  //设置B7为0

校验位设置

TB8:校验位,在串口模式23作为校验位,模式18bit的,无需校验位

发送数据

发送字节函数:

#include <reg52.h>void UartInit()                //9600bps@11.0592MHz
{SCON = 0x40;        //0100 0000 串口工作模式1TMOD &= 0x0F;       //清空TMOD中定时器1相关TMOD |= 0X20;       //设置定时器1工作模式2:8位自动重载TH1 = 0xFD;         //设定定时初值TL1 = 0XFD;TR1 = 1;            //启动定时器1
}//主函数
void main()
{UartInit();//调用串口初始化函数SBUF=0x30;while(1){}       
}

发送字符串函数:

这里还需要注意一个点是发送完成最后一位后,会置TI=1,所以我们可以用TI寄存器来判断是否发送完。

#include <reg52.h>void UartInit()             //9600bps@11.0592MHz
{SCON = 0x40;        //0100 0000 串口工作模式1TMOD &= 0x0F;       //清空TMOD中定时器1相关TMOD |= 0X20;       //设置定时器1工作模式2:8位自动重载TH1 = 0xFD;         //设定定时初值TL1 = 0XFD;TR1 = 1;            //启动定时器1
}void send_string(unsigned char str[])
{unsigned char i=0;while(str[i]!='\0')//判断是否到字符串尾{SBUF = str[i];while(TI==0);    //等待发送完成,发送完成TI会置1TI=0;            //下次发送前,要手动将TI置0i++;             //下次发送}       
}//主函数
void main()
{UartInit();//调用串口初始化函数send_string("hello world!!");while(1){}       
}

串口接收数据相关寄存器配置

波特率设置

波特率部分和发送是一样,于是初始化函数就变为

void UartInit()                //9600bps@11.0592MHz
{SCON = 0x40;            //0101 0000 串口工作模式1TMOD &= 0x0F;           //清空TMOD中定时器1相关TMOD |= 0X20;           //设置定时器1工作模式2:8位自动重载TH1 = 0xFD;             //设定定时初值TL1 = 0XFD;TR1 = 1;                //启动定时器1
}

允许接收寄存器设置

REN 禁止/允许串口接收控制位,为1时,才能接收。

SM0 = 0;
SM1 = 1;
REN = 1;//
允许接收数据

SCON =
0x50;                //0101 0000 串口工作模式1 允许接收

接收有效数据标志位

RI:接收有效数据标志位

串口中断

接收到有效数据时,会触发串口中断,这时候,我们开启串口中断相关的寄存器,就可以在中断服务函数中处理相关逻辑,不需要在大循环中一直判断啦。


#include <reg52.h>
sbit led=P2^7;//串口初始化函数
void UartInit()                //9600bps@11.0592MHz
{SCON = 0x50;                //0101 0000 串口工作模式1TMOD &= 0x0F;                //清空TMOD中定时器1相关TMOD |= 0X20;       //设置定时器1工作模式2:8位自动重载TH1 = 0xFD;                //设定定时初值TL1 = 0XFD;TR1 = 1;                //启动定时器1ES=1;//打开串行通信中断EA=1;//打开总中断
}
//串行中断函数
void Uart_receive() interrupt 4
{if(RI==1)//RI=1说明串口接收到了数据{                char receive;RI=0;//RI置0保证下次接收receive=SBUF;//将从串口接收到的数据报存到变量中//判断接收的数据,作出相应的操作if(receive=='O')led=0;if(receive=='C')led=1;       }
}
//主函数
void main()
{UartInit();while(1){}
}

Printf发送数据

#include <reg52.h>
#include <stdio.h> void UartInit(void)                //9600bps@11.0592MHz
{PCON &= 0x7F;                //波特率不倍速SCON = 0x50;                //8位数据,可变波特率TMOD &= 0x0F;                //清除定时器1模式位TMOD |= 0x20;                //设定定时器1为8位自动重装方式TL1 = 0xFD;                //设定定时初值TH1 = 0xFD;                //设定定时器重装值ET1 = 0;                //禁止定时器1中断TR1 = 1;                //启动定时器1
}/*
**重写printf调用的putchar函数,重定向到串口输出
**需要引入头文件<stdio.h>
*****/
char putchar(char dat){//输出重定向到串口SBUF = dat;     //写入发送缓冲寄存器while(!TI);    //等待发送完成,TI发送溢出标志位 置1TI = 0;      //对溢出标志位清零return dat;  //返回给函数的调用者printf
}//主函数
void main()
{UartInit();//调用串口初始化函数printf("hello\r\n");while(1){}       
}

printf格式化输出

c语言中的格式输出稍微一些不同
例如打印 unsigned char 类型的数据需要使用 %bd

unsigned char dat1 = 48;
printf("char-->%bd\r\n",dat1);   //无符号字符型使用%bd显示十进制数

http://www.xdnf.cn/news/213409.html

相关文章:

  • TTL、RS-232 和 RS-485 串行通信电平标准区别解析
  • 【C语言练习】010. 理解函数参数的传递方式
  • 深度解析Qwen3:性能实测对标Gemini 2.5 Pro?开源大模型新标杆的部署挑战与机遇
  • 牛客周赛 Round 91
  • k8s 学习记录 (六)_Pod 污点和容忍性详解
  • 日常开发小Tips:后端返回带颜色的字段给前端
  • 数据结构:实验7.3Huffman树与Huffman编码
  • 【18】爬虫神器 Pyppeteer 的使用
  • 信息科技伦理与道德3-4:面临挑战
  • 宾馆一次性拖鞋很重要,扬州卓韵酒店用品详细介绍其材质与卫生标准
  • 论文导读 - 基于特征融合的电子鼻多任务深度学习模型研究
  • 【无基础】小白解决Docker pull时报错:https://registry-1.docker.io/v2/
  • Html 2
  • verl - 火山引擎大语言模型强化学习训练库
  • Wi-SUN与LoRa和NB-IoT通信技术的对比
  • AI+零售:智能推荐、无人店与供应链管理的未来
  • 基于STM32、HAL库的DS28E15P安全验证及加密芯片驱动程序设计
  • Kafka 消息可靠性深度解析:大流量与小流量场景下的设计哲学
  • [逆向工程]如何理解小端序?逆向工程中的字节序陷阱与实战解析
  • 搜索引擎中的检索模型(布尔模型、向量空间模型、概率模型、语言模型)
  • 贵族运动项目有哪些·棒球1号位
  • CSR社会责任报告是什么?CSR社会责任报告定义
  • C++ 如何计算两个gps 的距离
  • 基于 ARM 的自动跟拍云台设计
  • 【无标题】好用的远程链接插件
  • 水安题库:水利水电安全员ABC精选练习题
  • 阿里巴巴Qwen3发布:登顶全球开源模型之巅,混合推理模式重新定义AI效率
  • 如何个人HA服务器地址和长期密钥
  • 精益管理是什么?如何才能实现精益管理?
  • WinSW注册服务