☰
80C51串行口详解:从内部结构到SCON/PCON及波特率计算初始化
2026/10/17 1:06:27 网站建设 项目流程

51学习第七天(二)——80c51串行口内部结构、SCON、PCON、工作方式、波特率计算与初始化,一篇讲透串口

学51到第七天,如果你还停留在点亮LED、驱动数码管、按键扫描这个阶段,那今天这一步算是正式踏入“单片机与外界对话”的门槛了。串行口(UART)是80C51里最常用、也最实用的外设之一,调试时打印一个变量、和电脑上位机通信、控制蓝牙/WiFi模块、接GPS模块、甚至和另一块单片机通信,全都靠它。

这篇文章我按自己的学习路径来写:先讲串口内部结构,再讲SCON和PCON这两个控制寄存器,然后对比四种工作方式,手把手算波特率,最后给出一套可以直接抄的初始化代码,外加我在实际调试中踩过的坑。内容偏基础,但都是干货,适合正在学51串口、被波特率搞晕、初始化总出问题的同学。

1. 串行口硬件结构:数据是怎么从单片机“说”出去的

很多人学串口上来就背寄存器,背完就忘。我建议先花十分钟搞清楚80C51串行口的内部硬件结构,知道数据是怎么从CPU走到引脚上的,后面SCON里每一位的作用就变得顺理成章了。

1.1 结构框图里那几个关键部件

80C51串行口在芯片内部主要由这几个部分构成:发送数据缓冲器SBUF、接收数据缓冲器SBUF(注意是两个独立物理单元)、发送移位寄存器、接收移位寄存器、串行控制逻辑,以及TXD(P3.1)和RXD(P3.0)两个引脚。

发送过程是这样的:CPU执行SBUF = data这条指令,把一个字节写入发送SBUF,硬件自动在移位时钟驱动下,把SBUF里的8位数据从低位到高位逐位搬到TXD引脚上,发送完毕后硬件把TI位置1。接收过程相反:RXD引脚上的电平变化被采样电路逐位移入接收移位寄存器,收够一帧后硬件自动把数据装入接收SBUF,然后把RI位置1,CPU执行data = SBUF就能读出来。

有一点非常关键:发送SBUF和接收SBUF虽然名字一样、地址也一样(都是0x99),但它们是两个完全独立的物理寄存器。写0x99只会写入发送SBUF,读0x99只会从接收SBUF读出,互不干扰。所以一条指令SBUF = x和另一条指令x = SBUF根本不会冲突。这是初学者最需要建立的概念。

1.2 双缓冲机制有什么用

很多人不理解为什么要在SBUF之外再加一个移位寄存器。直接让CPU往移位寄存器里写不行吗?

不行。假设没有中间缓冲,CPU正在往移位寄存器里填数据的时候,移位时钟恰好启动,就会把还没填完的字节发出去,产生错帧。有了双缓冲,CPU只需要把数据写进SBUF就立刻可以去干别的事了,移位寄存器慢慢往外发,发完置一个TI通知CPU“我这个字节已经发完了,可以再写下一个”。接收侧同理,移位寄存器收完一帧后把完整字节送到接收SBUF,这个拷贝是瞬时完成的,下一帧从RXD进来时不会覆盖上一次的数据,因为此时上一帧数据已经在SBUF里等着CPU来读了。

这里有个实际意义:在主循环里处理串口发送时,你不需要用延时让CPU等TXD引脚把数据发完,只要检测TI标志位即可,这个后面初始化代码部分会具体演示。

2. 串行口控制寄存器SCON:每一个比特都是总开关

SCON是串行口最核心的控制寄存器,地址0x98,支持位寻址,每一位都有一个位地址,可以直接用RI = 1、TI = 0这样的写法操作。下面把它拆开讲清楚。

2.1 SCON各位的功能与含义

SCON共8位:SM0、SM1、SM2、REN、TB8、RB8、TI、RI。

SM0和SM1是工作方式选择位,组合出四种工作方式:00对应方式0,01对应方式1,10对应方式2,11对应方式3。这四位组合是后面讲工作方式的基础,选择不同方式直接决定了帧格式和波特率来源。

SM2是多机通信允许位。在方式2和方式3下,若SM2=1,则只有当接收到的第9位数据(RB8)为1时,RI才置1,单片机才接收这一帧;RB8为0时直接丢弃。SM2=0时不管第9位是0还是1都正常接收。这个功能用在多机通信中作为地址帧和数据帧的区分手段。

REN是接收允许位,靠软件置1来使能接收,置0则禁止接收。注意:方式0下REN=0会关闭移位寄存器的移位功能,方式1~3下REN=0就是不听RXD引脚上的数据了。

TB8是发送的第9位数据位,在方式2和方式3中由软件赋值,常用于多机通信时发送地址帧(置1)或数据帧(置0)。RB8是接收到的第9位数据位,也只在方式2和方式3中有效。方式1下如果SM2=0,RB8存放接收到的停止位。

TI是发送中断标志位。发送完一帧数据后由硬件置1,注意硬件不会自动清零,必须由软件清零。TI=1有两种响应路径:如果打开了串口中断(ES=1),则触发中断;否则作为查询标志位,软件循环判断。

RI是接收中断标志位。接收完一帧数据后由硬件置1,同样需要软件清零。

2.2 为什么说TI和RI“必须手动清零”是最大的坑

不少初学者写串口程序时发现第一个字节发送正常,后面就乱套了,或者中断一进来就死循环,十有八九是没手动清TI/RI。

举个例子,主循环里你写了这样一个发送函数:

void UART_SendByte(unsigned char dat) { SBUF = dat; while(!TI); // 等待发送完成 }

第一次调用时寄存器上电复位,TI=0,数据写完SBUF,硬件开始发送,发完后TI被置1,循环退出,函数返回。看起来没问题。但如果这个函数被连续调用第二次、第三次呢?第二次进入时TI还是上一次遗留的1,while(!TI)瞬间就不等了,此时上一帧可能刚刚发完、SBUF虽然可以写入,但紧接着写入新数据时如果前一个字节还没完全移出,就会导致帧格式出问题。而且更直接的问题是,第二次调用SBUF = dat可能发生在上一帧数据还未完全发送完毕的时刻,导致数据覆盖。

正确写法是在等待TI置1之后立刻清零:

void UART_SendByte(unsigned char dat) { SBUF = dat; while(!TI); // 等发送完成 TI = 0; // 手动清标志 }

有人习惯先清TI再发送:

void UART_SendByte(unsigned char dat) { TI = 0; SBUF = dat; while(!TI); }

这两种写法都行,核心原则就是:发送完毕后必须把TI复位,不能让它一直为1。RI同理,读完数据后必须RI = 0,不然下一帧进来时RI已经为1,程序会误以为又收到新数据而重复处理上一次的数据。

3. 特殊功能寄存器PCON:SMOD位与波特率倍增的秘密

讲完SCON,还有一个和串口密切相关的寄存器——电源控制寄存器PCON,地址0x87。这个寄存器里和串口有关的只有最高位SMOD,它控制波特率是否加倍,实际使用中相当重要。

3.1 PCON的结构

PCON一共8位,最高位SMOD是串行口波特率倍增位。单片机复位时PCON各位清零,也就是SMOD=0。在方式1、方式2、方式3下,如果SMOD=1,波特率在原有基础上翻倍;SMOD=0则波特率不变。

PCON这个寄存器不可位寻址,也就是说你无法像操作SCON那样直接写SMOD = 1,必须整体赋值。比如要把SMOD置1而其他位不动,正确写法是:

PCON |= 0x80; // 只把最高位置1

为什么这里单独强调一下?因为真的有人在Keil里直接写SMOD = 1,编译报错后一脸懵。和SCON不同,PCON不支持位操作指令,只能用字节读写。

3.2 SMOD倍增的数学含义

波特率翻倍,本质上是把波特率时钟源的分频系数从64降为32。以方式1和方式3为例,波特率公式:

波特率 = (2^SMOD / 32) × 定时器1溢出率

SMOD=0时,2^0=1,除以32;SMOD=1时,2^1=2,除以16。分子从32变成16,正好翻倍。

方式2的固定波特率公式也一样:

波特率 = (2^SMOD / 64) × 振荡频率

注意这里分母是64,和方式1/方式3分母不同,后面计算的时候别搞混。

4. 四种工作方式:从同步移位到多机通信,到底怎么选

80C51串行口有四种工作方式,记住一句话:方式0用来扩展并行口,方式1是基础8位UART,方式2是固定波特率9位UART,方式3是可变波特率9位UART。平时用得最多的是方式1,需要多机通信时用方式3。

4.1 方式0:同步移位寄存器方式

方式0下串行口工作于同步移位寄存器模式,TXD引脚输出移位脉冲,RXD引脚输入或输出数据。每帧8位数据,低位在前,波特率固定为振荡频率的1/12。

这个方式一般不用于单片机之间的通信,而是用来外接移位寄存器芯片扩展并行I/O口,比如用74HC164做并行输出扩展,用74HC165做并行输入采集。它节省了I/O口,但代价是不能长距离传输、抗干扰差,适合板内短距离使用。

4.2 方式1:8位UART,最常用的通信方式

方式1的帧格式是1位起始位(低电平)+ 8位数据位(低位在前)+ 1位停止位(高电平),共10位。起始位和停止位由硬件自动添加。波特率由定时器1的溢出率决定,公式:

波特率 = (2^SMOD / 32) × 定时器1溢出率

其中定时器1溢出率就是定时器1每秒产生的溢出次数。这是最常用的通信方式,波特率可变、帧格式标准,和电脑串口助手直接通信完全没有问题。

4.3 方式2:9位UART,固定波特率

方式2的帧格式是1位起始位 + 8位数据位 + 1位可编程的第9位(TB8/RB8)+ 1位停止位,共11位。波特率是固定的,由振荡频率决定:

波特率 = (2^SMOD / 64) × 振荡频率

比如12MHz晶振、SMOD=0时,波特率 = 12MHz / 64 = 187500bps。这个速率太高,和大多数外设通信都不匹配,所以方式2在实际项目里很少用,除非你的晶振频率恰好能产生目标波特率。

4.4 方式3:9位UART,可变波特率+多机通信

方式3的帧格式和方式2完全一样,都是11位,但波特率来源变成了定时器1溢出率,公式和方式1一样:

波特率 = (2^SMOD / 32) × 定时器1溢出率

所以方式3兼具9位帧格式和可调波特率两个优点。多机通信时通常选择方式3,配合SCON里的SM2位和TB8/RB8来实现地址帧/数据帧的区分。

4.5 四种方式对比速查

工作方式帧格式波特率来源典型用途
方式08位同步数据振荡频率/12扩展并行I/O口
方式1起始位+8数据+停止位定时器1溢出率常规双机/PC通信
方式2起始位+8数据+TB8/RB8+停止位固定频率/64或/32较少使用
方式3起始位+8数据+TB8/RB8+停止位定时器1溢出率多机通信、常规通信

5. 波特率计算:公式、实例与误差分析

波特率计算是串口学习里最容易劝退人的环节,其实核心就一个公式、一个初值换算。我彻底把它讲透。

5.1 核心公式与定时器初值推导

方式1和方式3的波特率公式:

波特率 = (2^SMOD / 32) × 定时器1溢出率

定时器1溢出率的计算要看定时器工作方式。串口波特率生成最常用的方式是定时器1工作于方式2(8位自动重装),此时:

定时器1溢出率 = 定时器1计数频率 / (256 - TH1)

如果定时器1的计数频率是振荡频率的1/12(12T模式),则:

定时器1溢出率 = 振荡频率 / 12 / (256 - TH1)

代入波特率公式:

波特率 = (2^SMOD / 32) × 振荡频率 / 12 / (256 - TH1)

化简后得到(12T模式下):

波特率 = (2^SMOD × 振荡频率) / [384 × (256 - TH1)]

反过来求TH1:

TH1 = 256 - (2^SMOD × 振荡频率) / (384 × 波特率)

这个公式一定要自己推导一遍,别看它有点绕,理解了之后再遇到任何晶振频率、任何波特率,你都能自己算初值,而不是永远查表。

5.2 实战计算:11.0592MHz晶振、9600bps

最常见的晶振是11.0592MHz,为什么选这个频率?因为它能精确分频出众多标准波特率。以9600bps、SMOD=0、12T模式为例:

TH1 = 256 - (1 × 11059200) / (384 × 9600) = 256 - 11059200 / 3686400 = 256 - 3 = 253 = 0xFD

所以直接把TH1和TL1都填0xFD,就能得到9600bps。

再验算一下实际波特率:

波特率 = 11059200 / [384 × (256 - 253)] = 11059200 / 11520 = 9600

正好等于9600,零误差。这就是11.0592MHz晶振被称为“串口专用晶振”的原因。

如果SMOD=1,则:

TH1 = 256 - (2 × 11059200) / (384 × 9600) = 256 - 6 = 250 = 0xFA

同样能得到9600bps,但此时定时器溢出率更高,初值更小。

5.3 如果只有12MHz晶振怎么办

很多开发板用的是12MHz晶振,此时算9600bps:

TH1 = 256 - 12000000 / (384 × 9600) = 256 - 3.255208 = 252.7448

TH1只能是整数,取253:

实际波特率 = 12000000 / [384 × (256 - 253)] = 12000000 / 11520 = 10416.67bps

误差 = (10416.67 - 9600) / 9600 ≈ 8.5%

这个误差已经超过UART可容忍的范围了。UART异步通信要求收发双方波特率误差一般在2%~3%以内,8.5%的偏差会导致接收端采样点逐渐偏移,轻则偶尔乱码,重则完全收不到。所以工程实践上,只要涉及串口通信,尽量选11.0592MHz晶振。12MHz晶振做串口通信不是不行,得用非标准波特率或者换其他分频方案,但麻烦事多,不如直接换晶振。

5.4 波特率误差检查表

晶振频率目标波特率计算TH1(SMOD=0)实际波特率误差
11.0592MHz96000xFD96000%
11.0592MHz48000xFA48000%
11.0592MHz192000xFD(SMOD=1)192000%
12MHz96000xFD10416.67+8.5%
12MHz48000xF948000%

看到没,12MHz算4800bps反而没误差,因为12000000 / (384 × 4800) = 6.51,取整后不是精确值,这里表格里取的0xF9对应的是初值247,实际算下来是有微小误差的,我标注一下:12000000/(384×(256-247))=12000000/3456≈3472bps,这是不对的。正确算4800的初值:12000000/(384×4800)=6.51,TH1=256-6.51≈249.49,取249,实际波特率=12000000/(384×7)=4464bps,误差约7%,更糟糕。

所以再次强调:12MHz晶振做标准波特率串口通信非常吃力,老老实实用11.0592MHz。

5.5 6T模式下的计算

增强型51单片机(比如部分STC系列)可以通过配置将机器周期从12个时钟降为6个甚至1个,此时波特率公式里的12要换成6或1。

6T模式下,方式1/方式3的波特率公式变为:

波特率 = (2^SMOD × 振荡频率) / [192 × (256 - TH1)]

11.0592MHz、9600bps、SMOD=0:

TH1 = 256 - 11059200 / (192 × 9600) = 256 - 6 = 250 = 0xFA

所以同样硬件配置,6T模式下的TH1初值和12T模式SMOD=1时的初值一样。这一点在实际写代码之前务必确认你的单片机到底工作在12T还是6T模式,不然照抄别人代码时初值全对但波特率翻倍,串口收到一堆乱码还找不到原因。

6. 串口初始化实操:完整代码与逐步拆解

理论讲完,下面给出可以直接抄的初始化代码。我以Keil C51、11.0592MHz晶振、9600bps、方式1、查询方式收发为例。

6.1 初始化步骤清单

串口初始化可以归纳为固定四步:

  1. 配置定时器1工作方式:TMOD &= 0x0F,再TMOD |= 0x20,让定时器1工作于方式2(8位自动重装)。
  2. 设置波特率初值:TH1 = TL1 = 0xFD,然后TR1 = 1启动定时器。
  3. 设置串口工作方式:SCON = 0x50,对应SM0=0、SM1=1、SM2=0、REN=1,即方式1,允许接收。
  4. 必要时设置PCON的SMOD位,然后配置中断(ES、EA)。

第2步里有一个细节容易被忽略:TMOD的复位值是0x00,定时器0和定时器1都被配置为方式0(13位定时器),直接写TMOD=0x20会同时把定时器0也改成方式1,覆盖你之前对定时器0的配置。所以稳妥写法是先清低四位、再置高四位:TMOD &= 0x0F; TMOD |= 0x20;。同理,如果你定时器0正用于定时,这个细节能省去一堆排查时间。

6.2 完整代码示例

#include <reg52.h> void UART_Init(void) { TMOD &= 0x0F; // 清空定时器1的模式位 TMOD |= 0x20; // 定时器1工作于方式2:8位自动重装 TH1 = 0xFD; // 波特率初值:9600bps @ 11.0592MHz, SMOD=0 TL1 = 0xFD; // 自动重装值 TR1 = 1; // 启动定时器1 SCON = 0x50; // 方式1,REN=1允许接收 PCON &= 0x7F; // SMOD=0,波特率不倍增 } void UART_SendByte(unsigned char dat) { SBUF = dat; while(!TI); // 等待发送完成 TI = 0; // 清零发送标志 } unsigned char UART_ReceiveByte(void) { while(!RI); // 等待接收 RI = 0; // 清零接收标志 return SBUF; } void UART_SendString(unsigned char *str) { while(*str) { UART_SendByte(*str++); } } void main(void) { UART_Init(); while(1) { UART_SendString("Hello UART\r\n"); // 等待接收直到有数据到来,收到后原样回显 if(RI) { unsigned char tmp = UART_ReceiveByte(); UART_SendByte(tmp); } } }

这段代码的核心逻辑很简单:每轮循环先发一条字符串,然后检查RI有没有置位,如果有就接收并回显。注意主循环里判断的是RI,但UART_ReceiveByte内部又用while(!RI)等待,这里如果RI已经为1会直接通过。实际上更简洁的写法是主循环直接判断RI后读取SBUF,不一定封装函数,看需求灵活处理。

6.3 查询方式与中断方式的取舍

上面的代码用的是查询方式。查询方式代码简单、逻辑直观,适合单任务或主循环不那么繁忙的程序。但存在一个明显问题:如果主循环正在执行一段耗时操作(比如数码管动态扫描、长时间的延时),串口数据来了你没法及时响应,数据可能被下一帧覆盖丢失。

中断方式则不同。初始化里打开ES和EA,在串口中断服务函数里直接处理收到或者发送完成的事件:

void UART_Init(void) { // 前面的配置照旧 ES = 1; // 打开串口中断 EA = 1; // 打开总中断 } void UART_ISR(void) interrupt 4 { unsigned char dat; if(RI) { RI = 0; dat = SBUF; UART_SendByte(dat); // 回显 } if(TI) { TI = 0; // 发送完成,可在此继续发送下一帧 } }

注意串口中断服务函数判断RI和TI的顺序:先判RI再判TI。因为在同一时刻只可能有一个标志位置1,但严谨起见两个都判断。中断方式的好处是主循环不必频繁轮询RI,数据到了硬件自动打断CPU,程序响应及时。代价是中断服务函数里尽量不要做耗时操作,否则会影响主循环的实时性。

6.4 发送字符串的细节

串口发送字符串时经常遇到两个小问题。第一,字符串末尾忘了加\r\n,导致上位机收到的内容不换行,显示成一长串。第二,使用while(*str)判断字符串结尾,要求你传入的确实是以\0结尾的字符串常量或者数组,如果传入一个没有结尾符的数组,程序会一直发到内存里的任意0为止,看起来就是发送了一堆乱码。

实际项目里我习惯把发送字符串改为指定长度的发送,或者在数组定义时就保证有结尾符:

code unsigned char msg[] = "System ready\r\n";

如果字符串存放在代码区(code区),注意传入函数时要声明unsigned char code *str,否则指向数据区的指针去读代码区数据,拿到的是错误的地址,发送内容会变成一堆未知字符。这也是Keil C51里新手经常踩的坑。

7. 常见问题与排查实录:串口调不通,到底哪儿错了

串口调试不像点灯,出了问题有迹可循。我把自己调试串口时遇到过的几类典型问题整理在下面,每个都给出排查思路。这些问题如果没遇到过,你可能会觉得“怎么可能会犯这种错”,但实际就是在这些细节上翻车的人最多。

7.1 收到乱码:波特率、晶振、SCON三连查

乱码是最常见的问题。先确认收发双方的波特率是否一致。电脑串口助手里选的是9600,单片机初始化也是9600,这个提前对好。然后确认波特率初值是否匹配晶振频率,12MHz晶振配9600bps就是天然8.5%误差,乱码正常,换11.0592MHz晶振。最后确认SCON配置是否正确,方式1应该设0x50,如果你误设成0x10(REN没置1),接收功能没开,也会表现为收不到或者读到错误的数。

还有一种情况:发送端波特率是对的,接收端用的不是UART而是逻辑分析仪的默认解码配置,解码失败显示乱码。这时候先拿串口助手自收自发验证,排除上位机问题后再查单片机。

7.2 只能发不能收:查REN,查引脚,查共地

接收不到数据,先检查REN位是否为1。REN=0时接收关闭,RI永远不会置1。这个最容易被忽略。

然后检查TXD和RXD有没有接反。单片机TXD接对方RXD,RXD接对方TXD,这是交叉连接的。很多人直接用一根杜邦线把两个TXD连在一起,当然通信不了。

第三个隐蔽的坑是共地问题。如果两块板子各自独立供电,一定要把GND连在一起,否则信号没有参考电平,UART收到的全是噪声。我自己有一阵子用USB转串口模块调试,模块的GND没和开发板共地,结果接收全乱码,接上GND后一切正常。共地问题在串口通信里出现的频率比你想象的高得多。

7.3 发送第一个字节丢失

程序上电后第一次调用UART_SendByte发送时,如果第一个字节丢了或者变成空数据,通常是因为单片机上电复位后TXD引脚电平还没稳定,上位机串口已经打开,复位瞬间TXD被拉低,被视为一个起始位,导致上位机误收一个0x00。解决办法是在初始化完成后延时几十毫秒再开始发送数据,或者上位机那边在上电后再打开串口。

7.4 一帧数据里偶尔多个字节或丢字节

这通常和波特率误差有关,但误差在2%以内时不应该出现偶发错误。肉眼可见的偶发错误更可能是时钟源不稳定,比如板子上用的直插晶振旁边没有负载电容,或者用了内部RC振荡器做波特率时钟。内部RC振荡器的精度受温度影响明显,做串口通信强烈建议用外部晶振。

7.5 收发都正常但上位机显示的是十六进制

串口助手显示模式选错也会让人误以为乱码。发送0x41时,文本模式显示A,十六进制模式显示41。如果上位机和单片机约定的是文本协议,显示十六进制当然看不懂。先把显示模式切换回来,再核对协议格式,别一上来就怀疑波特率。

7.6 常见问题速查表

现象可能原因排查方法
完全乱码波特率不一致收发双方统一波特率
完全乱码晶振频率与初值不匹配用11.0592MHz晶振
完全乱码未共地连接GND
完全乱码12T/6T模式配置不一致确认单片机的时钟模式
收不到数据REN=0初始化中SCON置0x50
收不到数据TXD/RXD接反交叉连接,TXD接RXD
第一个字节丢失上电复位时序初始化后延时再发送
偶发错帧时钟源不稳定改用外部晶振
能发不能收对方未打开串口先验证上位机收发
发送内容不对指针指向错误存储区区分data区与code区

7.7 排查串口的正确顺序

如果你实在调不通,我建议按这个顺序走一遍:

  1. 用一个USB转串口模块把单片机的TXD和RXD短接(环回测试),串口助手发送什么单片机收到什么,如果不行,问题在单片机侧;如果行,问题在连接线或对方设备。
  2. 用示波器或者逻辑分析仪看TXD引脚上有没有波形,有波形说明数据真的发出去了,没有波形则检查定时器TR1是否启动、SBUF是否成功写入。
  3. 把波特率降到2400或1200再试,低速下容错性更强,如果低速下正常、高速下乱码,说明是波特率误差或信号完整性问题。

环回测试这个技巧特别实用,它能快速把问题定位到“单片机内部”还是“外部链路”,避免在错误的方向上白白浪费时间。

8. 写在最后:串口调试的一点经验

我在学串口的过程中,真正花时间最多的不是理解SCON和PCON,也不是背波特率公式,而是排查那些“看起来正常但实际就差一步”的细节。比如共地、TXD和RXD接反、还有定时器模式被覆盖。这些坑几乎每个人都踩过,但网上很少有文章把细节讲全,很多教程直接贴代码,读者抄完运行不了也不知道差在哪。

经验是:学串口不要一上来就背寄存器表,先理解数据通路,再逐个分析控制位,然后自己动手算一次波特率初值,最后把发送、接收、中断三套代码各写一遍。这样过一遍之后,你以后再看到任何一篇串口文章,都会觉得特别轻松。

后续如果你要用单片机接ESP8266、GPS模块、蓝牙模块这些设备,串口就是你操作它们的窗口。把这套基础打牢,后面不管是做物联网还是做小型控制系统,都会顺畅很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询