【51单片机】串口通信原理 + 使用

学习使用的开发板:STC89C52RC/LE52RC
编程软件:Keil5
烧录软件:stc-isp

开发板实图:
在这里插入图片描述

文章目录

  • 串口
    • 硬件电路
    • UART
    • 串口相关寄存器
  • 编码
    • 单片机通过串口发送数据
    • 电脑通过串口发送数据控制LED灯

串口

  • 串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信
  • 单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大的扩展了单片机的应用范围,增强了单片机系统的硬件实力
    51单片机内部自带UART(Universal Asynchronous Receiver Transmitter,通用异步收发器),可实现单片机的串口通信

在这里插入图片描述

硬件电路

简单双向串口通信有两根通信线(发送端TXD(Transmit Exchange Data)和接收端RXD(Receive Exchange Date))

TXD和RXD要交叉连接,设备1的TXD连接设备2的RXD,设备1的RXD连接设备2的TXD
在这里插入图片描述
当只要单向传输数据时,可以只有一根通信线

GND是一定要连接的,若两个设备都可以各自供电,则不需要连接VCC

电平标准不一致时,还需要加电平转换芯片


电平标准

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

  • TTL电平:+5V表示1,0V表示0,一般用于单片机
  • RS232电平:-3 ~ -15V 表示1,+3 ~ +15V 表示0,一般用于电脑
  • RS485电平:两线压差 +2 ~ +6V 表示1,-2 ~ -6V 表示0(差分信号),一般用于CAN总线

前两个通信距离都较近,一般只有十几米,距离过远传输的数据就很容易出错;而RS485通信距离远,信号可靠性高,传输距离可达1KM以上


相关术语

  • 全双工:通信双方可以在同一时刻互相传输数据

在这里插入图片描述

  • 半双工:通信双方可以互相传输数据,但同时间只能有一段发送,另一端接收,必须分时复用一根数据线

在这里插入图片描述

  • 单工:通信双方只能一方发送到另一方,不能反向传输。比如遥控器控制电视,只能遥控器向电视发送数据
    在这里插入图片描述
  • 异步:通信双方各自约定通信速率
  • 同步:通信双方靠一根时钟线来约定通信速率

数据传输是依靠高低电平的,也就是电平协议。比如如下两个数据
在这里插入图片描述
看电平都是从高电平变为低电平,但是如果通信速率不一样,则获取的数据不一样
在这里插入图片描述
通信速率快,10可能会被解析为1100;通信速率慢,1100也可能会被解析为10。

所以约定好通信速率很重要

  • 总线:连接各个设备的数据传输线路(类似一条马路,把路边的住户连接起来,使住户可以相互交流)

在这里插入图片描述


常见通信接口比较

在这里插入图片描述

  • UART:为本节学习的串口通信接口
  • I2C:板子上的C24C02使用该串口
  • SPI:板子上的DS1302使用非标准SPI
  • 1-Wire:板子上的DS18B20使用该串口

常见的还有CAN总线USB,CAN总线常用于汽车领域


UART

51单片机的UART

STC89C52有1个UART,RXD和TXD分别和P3.0和P3.1这两个I/O口共用同一个引脚
在这里插入图片描述

STC89C52的UART有四种工作模式:
模式0:同步移位寄存器
模式1:8位UART,波特率可变(常用)
模式2:9位UART,波特率固定
模式3:9位UART,波特率可变


串口参数及时序图

  • 波特率:串口通信的速率(发送和接收各数据位的间隔时间)
  • 校验位:用于数据验证,一定程序可以知道数据是否错误,也需要双方提前协商统一校验。常用的校验如01校验奇偶校验9位UART就是多了一位校验位
  • 停止位:用于数据帧间隔,发送多个数据,如何间隔两个数据,就使用停止位
    波特率就是上述异步双方要约定好的如何对数据进行采样

串口收发数据,都是从低位开始
在这里插入图片描述

串口模式图

简单的串口模式图

在这里插入图片描述

UART是集成在单片机内部的,通过TXD引脚发送数据,RXD引脚接收数据

UART可以分为三个部分,中间为定时器T1——控制波特率;左侧绿框的SBUF用于收发数据;右侧为中断系统

SBUF:串口数据缓存寄存器,物理上是两个独立的寄存器,但占用相同的地址。写操作时,写入的是发送寄存器,读操作时,读出的是接收寄存器

完整的串口和中断系统模式图如下:

在这里插入图片描述

下面通过介绍相关寄存器来讲述串口通信的原理

串口相关寄存器

在这里插入图片描述

串行口控制寄存器SCON和PCON

STC89C52系列单片机的串行口设有两个控制寄存器:串行控制寄存器SCON波特率选择特殊功能寄存器PCON

PCON
电源控制寄存器,可能是电源控制还有剩下比特位,所以波特率选择和帧错误控制位也集成其中,减少资源消耗
格式如下:
在这里插入图片描述
我们只要关注SMODSMOD0 即可

  • SMOD:波特率选择位。当软件置SMOD = 1时,使串行通信方式1、2、3的波特率加倍;SMOD = 0,则不加倍。复位时SMOD = 0
  • SMOD0:帧错误检测有效控制位。当SMOD = 1,SCON寄存器中的 SM0/FE 位于 FE(帧错误检测)功能;当SMOD0 = 0,SCON的SM0/FE 用于 和 SM1 组合指定串行口的工作模式。复位时 SMOD0 = 0

SCON
用于选择串行通信的工作方式和某些控制功能,格式如下:
在这里插入图片描述

  • SM0/FE:当PCON寄存器的 SMOD0/FE = 1时,该位用于帧错误检测。当检测到一个无效停止位时,通过UART接收器设置该位,必须由软件清零;当SMOD0/FE = 0,该位和 SM1 组合指定工作模式

在这里插入图片描述

  • REN:允许/禁止串行接收控制位。由软件置位,若REN = 1即允许串行接收数据;REN = 0则禁止接收

  • SM2:允许方式2或方式3多机通信控制位

  • TB8:在方式2或方式3,为要发送的第9位数据,按需要由软件置位或清0。可用作数据的校验位或多机通信中表示地址帧/数据帧的标志位。

  • RB8:在方式2或方式3,是接收到的第9位数据。在方式1,若SM2=0,则RB8是接收到的停止位。方式0不用RB8。

  • TI:发送中断请求标志位。在方式0,当串行发送数据第8位结束时,由内部硬件置TI = 1,向主机请求中断,响应中断后必须用软件复位,即TI = 0。在其他方式中,则在停止位开始发送时由内部硬件置位必须用软件复位TI = 0

  • RI:接收中断请求标志位。在方式0,当串行接收到第8位结束时由内部硬件自动置位 RI = 1 ,向主机请求中断,响应中断后必须用软件复位,即RI=0。在其他方式中,串行接收到停止位的中间时刻由内部硬件置位,即RI=1,必须由软件复位,即RI=0

IE
中断允许寄存器
在这里插入图片描述
其中我们只关注EAES

  • EA:CPU的总中断允许控制位,EA = 1,CPU开放中断;EA = 0,CPU屏蔽所有中断请求
  • ES:串行口中断允许位,ES = 1,允许串行口中断;ES = 0,禁止串行口中断

在这里插入图片描述

注意:接收中断和发送中断共用一个中断,在中断处理函数中还需要通过RITI 的置位判断本次中断是接收中断还是发送中断


最后我们回归模式图,讲解一下串口通信的流程

在这里插入图片描述

发送数据
通过总线将数据写入SBUF,定时器1控制波特率。通过TXD发送数据,当发送数据结束时(方式0为发完8位数据,其他方式为发送停止位时),将 TI = 1,发送中断请求
接收数据
RXD接收数据,通过定时器1控制波特率,对接收数据进行采样,存放在SBUF,当接收数据结束(方式0当串行发送数据第8位结束时,在其他方式中,则在停止位开始发送时由内部硬件置位),将 RI = 1,发出中断请求

注意TIRI 都需要由软件置0

编码

经过上述学习,我们已经对串口有了一定的了解,接下来就是实现串口通信

单片机通过串口发送数据

我们使用UART串口通信,首先要进行初始化,如:选择工作方式,初始化中断系统,设置波特率,初始化定时器

选择工作模式
涉及到SCONPCON

首先是PCON的SMOD0,当 SMOD0 = 0 时,SMOD 的 SM0 才会被用来选择工作方式
其次,我们选择 8位UART,波特率可变模式,即方式1,SCON的SM0 = 0,SM1 = 1
最后,如果要允许串口接收数据,还需要置SCON的REN = 1


初始化中断系统

首先,初始化串口收发数据的中断请求标志位,SCON的 TIRI,由硬件置1,我们初始化时清零即可:TI = 0, RI = 0
到此,SCON的设置就结束了
总结一下,SM0 = 0, SM1 = 1, REN = 1/0, TI = 0, RI = 0,其他默认为0即可,所以SCON = 0x40/0x50

然后是中断开关
在这里插入图片描述

ES = 1, EA = 1

SCON = 0x50;	//选择工作方式 & 允许串口接收数据
PCON |= 0x80;	//使SM0为选择工作方式
//中断开关
ES = 1;			//串口中断开关
EA = 1;			//总中断开关

初始化定时器

初始化定时器可参看【51单片机】定时器
此处定时器1选择工作模式2——8位自动重装

8位自动重装
一次只对TL1或TH1计数加一
当一个溢出后,直接使用另一个计数单元的初值

//设置定时器1
TMOD &= 0x0F;	//高4位清零
TMOD |= 0x20;	//0010,模式3——8位自动重载
TR1 = 1;		//启用定时器T1
ET1 = 0;		//禁止定时器T1中断	
//定时器初值
TL1 = 0xF3;		//设定定时初值
TH1 = 0xF3;		//设定定时器重装值

设置波特率
我们设置波特率为4800
设置波特率需要通过设置定时器1的初始值

TL1 = 0xF3;		//设定定时初值
TH1 = 0xF3;		//设定定时器重装值

讲解一下为什么定时器初值是这个

假设系统频率为12MHz,使用12T模式,则定时器频率为12 / 12 = 1MHz,即每1us,计数单元加1。

使用8位自动重装,256时会溢出,0xF3 = 243,256 - 243 = 13。所以定时器溢出需要13us

溢出率:1 / 13 = 0.07692

使用SMOD = 1,波特率加倍(不除2)
在这里插入图片描述

还需要 0.07692 / 16 = 0.0048076923MHz
转化为Hz:4807.6923Hz,这个就是波特率
会存在一定误差

也可参看如下计算
在这里插入图片描述


到此,串口的初始化就完成了
完整代码如下:

/*** @brief		初始化串口* @parm		无* @retval		无*/
void UART_Init()
{//SCON高4位分别为SM0、SM1、SM2、REN//SM0和SM1控制串口模式,选择01——8位UART,波特率可变//REN接收使能,REN = 0禁止接收,REN = 1允许接收//所以设置0101 0000SCON = 0x50;//PCON包含波特率和电源设置//前两位为SMOD和SMOD0//SMOD = 1波特率加倍,SMOD = 0,波特率不加倍//SMOD0是帧错误的,此处不用//所以设置1000 0000PCON |= 0x80;//设置定时器1TMOD &= 0x0F;	//高4位清零TMOD |= 0x20;	//0010,模式3——8位自动重载TR1 = 1;		//启用定时器T1ET1 = 0;		//禁止定时器T1中断	//定时器初值TL1 = 0xF4;		//设定定时初值TH1 = 0xF4;		//设定定时器重装值//中断开关ES = 1;			//串口中断开关EA = 1;			//总中断开关
}

博主的单片机系统频率为11.0592MHz
可以使用STC-ICP生成波特率设置代码

在这里插入图片描述

注意:配置一定要选择正确;代码中的AUXR寄存器为高版本单片机才有的,低版本不认识这个寄存器,可以直接删掉


串口发送数据通过赋值SBUF,数据发送完后,硬件置位TI = 1,需要我们手动对TI清零
代码如下:

/*** @brief 		通过串口发送一个字节数据* @parm		Byte:要发送字节数据* @retval		无*/
void UART_SendByte(unsigned char Byte)
{SBUF = Byte;while(TI == 0);//数据发送完,硬件置1TI = 0;		//软件置0
}

模块化编程,完整代码如下:

延时模块——控制串口发送数据速率
Delay.h

#ifndef __DELAY_H__
#define __DELAT_H__void Delayms(unsigned int xms);//等待指定毫秒#endif

Delay.c

#include <INTRINS.h>
/*** @brief  延迟一定时间* @parm	延迟的时间,单位是毫秒,范围:0 ~ 65535* @retval	无*/
void Delayms(unsigned int xms)		//@11.0592MHz
{while(xms--){unsigned char i, j;_nop_();i = 2;j = 199;do{while (--j);} while (--i);}	
}

UART串口模块

UART.h

#ifndef __UART_H__
#define __UART_H__void UART_Init();
void UART_SendByte(unsigned char Byte);#endif

UART.c

#include <REGX52.H>
/*** @brief		初始化串口* @parm		无* @retval		无*/
void UART_Init()
{//SCON高4位分别为SM0、SM1、SM2、REN//SM0和SM1控制串口模式,选择01——8位UART,波特率可变//REN接收使能,REN = 0禁止接收,REN = 1允许接收//所以设置0101 0000SCON = 0x50;//PCON包含波特率和电源设置//前两位为SMOD和SMOD0//SMOD = 1波特率加倍,SMOD = 0,波特率不加倍//SMOD0是帧错误的,此处不用//所以设置1000 0000PCON |= 0x80;//设置定时器1TMOD &= 0x0F;	//高4位清零TMOD |= 0x20;	//0010,模式3——8位自动重载TR1 = 1;		//启用定时器T1ET1 = 0;		//禁止定时器T1中断	//定时器初值TL1 = 0xF4;		//设定定时初值TH1 = 0xF4;		//设定定时器重装值//中断开关ES = 1;			//串口中断开关EA = 1;			//总中断开关
}
/*** @brief 		通过串口发送一个字节数据* @parm		Byte 要发送字节数据* @retval		无*/
void UART_SendByte(unsigned char Byte)
{SBUF = Byte;while(TI == 0);//数据发送完,硬件置1TI = 0;		//软件置0
}///**
//  * @brief 		接收数据 模版
//  * @parm			无
//  * @retval		无
//  */
//void UART_Routine() interrupt 4
//{
//	if(RI == 1)//检测是否是接收数据中断
//	{
//		RI = 0;//软件置0
//	}
//}

主程序——每隔一秒通过串口发送递增数据

#include <REGX52.H>
#include "UART.h"
#include "Delay.h"
/*** @brief		通过串口每隔1s发送递增的数据 范围:0 ~ 255* @parm		无* @retval		无*/
void SendIncreasingNum()
{static unsigned char num;UART_SendByte(num++);Delayms(1000);
}void main()
{UART_Init();while(1){SendIncreasingNum();}
}

使用STC-IST串口助手 查看效果

在这里插入图片描述
注意:下面一行的配置要正确

电脑通过串口发送数据控制LED灯

电脑发送数据给单片机需要USB转串口,自带的USB线就已经实现了这一转换,所以我们直接编写单片机通过串口接收数据的逻辑即可。

串口接收数据会存放在SBUF,接收完毕后会将RI置1,发出中断请求,中断号为4,然后需要手动清零RI

代码如下:

void UART_Routine() interrupt 4
{if(RI == 1)//检测是否是接收数据中断{P2 = SBUF;RI = 0;//软件置0}
}

注意P2寄存器用于控制LED亮灭,为0亮起,为1熄灭

还可以将数据重新返回给电脑,同样使用DelayUART 模块,只有main.c不同

main.c

#include <REGX52.H>
#include "UART.h"
#include "Delay.h"
/*** @brief 		接收数据,亮相应的灯,并返回数据* @parm		无* @retval		无*/
void UART_Routine() interrupt 4
{if(RI == 1)//检测是否是接收数据中断{P2 = SBUF;UART_SendByte(SBUF);RI = 0;//软件置0}
}
void main()
{UART_Init();while(1){}
}

效果如下:
我们通过串口助手,发送 0xAA = 1010 1010

在这里插入图片描述

LED灯效果如下:

在这里插入图片描述


以上就是本篇博客的所有内容,感谢你的阅读
如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/466262.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式web开发:boa、lighttpd

嵌入式web开发&#xff1a;boa、lighttpd https://blog.csdn.net/m0_37105371/category_10937068.html BOA服务器的移植-CSDN博客 【第1部分&#xff1a;boa服务器部署到ubuntu里】 http://www.boa.org/boa-0.94.13.tar.gz tar xvzf boa-0.94.13.tar.gz cd boa-0.94.13/src/ a…

RC高通滤波器Bode图分析(传递函数零极点)

RC高通滤波器 我们使得R1K&#xff0c;C1uF&#xff1b;电容C的阻抗为Xc&#xff1b; 传递函数 H ( s ) u o u i R X C R R 1 s C R s R C 1 s R C &#xff08;其中 s j ω &#xff09; H(s)\frac{u_{o} }{u_{i} } \frac{R }{X_{C}R} \frac{R }{\frac{1}{sC}R} \fra…

Python决策树、随机森林、朴素贝叶斯、KNN(K-最近邻居)分类分析银行拉新活动挖掘潜在贷款客户附数据代码

Python决策树、随机森林、朴素贝叶斯、KNN&#xff08;K-最近邻居&#xff09;分类分析银行拉新活动挖掘潜在贷款客户|附数据代码 最近我们被客户要求撰写关于银行拉新活动的研究报告&#xff0c;包括一些图形和统计输出。 项目背景&#xff1a;银行的主要盈利业务靠的是贷款&…

撰写开发信利器,高效工具助你赢在起点

ZohoCampaigns是电子邮件营销平台&#xff0c;提供创建、发送和分析邮件方案。其优势包括易用性、丰富模板、精准筛选、自动化和详细报告。外贸人员可用其高效发送开发信&#xff0c;追踪效果并优化策略&#xff0c;促进业务增长。 一、为什么选择Zoho Campaigns&#xff1f; …

协程5 --- 栈切换

文章目录 ucontext相关函数例子ucontext_t结构 setjump、longjump相关函数例子jmp_buf结构 汇编实现解析图示 ucontext 相关函数 #include <ucontext.h> int getcontext(ucontext_t *ucp);初始化ucp结构体&#xff0c;将当前上下文保存在ucp中。 int setcontext(const …

【Linux】Pinctrl子系统和GPIO子系统

Pinctrl子系统 在许多soc内部包含了多个pin控制器&#xff0c;通过pin控制器的寄存器&#xff0c;我们可以配置一个或者一组引脚的功能和特性。Linux内核为了统一各soc厂商的pin脚管理&#xff0c;提供了pinctrl子系统。该系统的作用&#xff1a; 在系统初始化的时候&#xf…

《Vue3 报错》Uncaught TypeError: s.finally is not a function

解决方案&#xff1a; 新建文件 my-polyfill.js // 当浏览器环境不支持Promise.prototype.finally if (!Promise.prototype[finally]) {Promise.prototype[finally] function(callback) {let P this.constructor;return this.then(value > P.resolve(callback()).then(…

RabbitMQ 七种工作模式介绍

目录 1.简单模式队列 2.WorkQueue(⼯作队列) 3 Publish/Subscribe(发布/订阅) 4 Routing(路由模式) 5.Topics(通配符模式) 6 RPC(RPC通信) 7 Publisher Confirms(发布确认) RabbitMQ 共提供了7种⼯作模式供我们进⾏消息传递,接下来一一介绍它的实现与目的 1.简单模式队列…

数组类算法【leetcode】

704. 二分查找 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 二分查找 用于有序数组中&#xff0c;没有重复的数组。…

Pandas 数据分析工具详细教程

Pandas 数据分析工具详细教程 Pandas 是一个强大的 Python 数据分析库&#xff0c;广泛应用于数据科学、数据分析和机器学习等领域。它提供了高效的数据操作和分析功能&#xff0c;使得数据处理变得简单而高效。本文将详细介绍 Pandas 的基本概念、数据结构、常用操作及其在数…

基于 EventBridge + DashVector 打造 RAG 全链路动态语义检索能力

作者&#xff1a;肯梦 本文将演示如何使用事件总线&#xff08;EventBridge&#xff09;&#xff0c;向量检索服务&#xff08;DashVector&#xff09;&#xff0c;函数计算&#xff08;FunctionCompute&#xff09;结合灵积模型服务 [ 1] 上的 Embedding API [ 2] &#xff0…

GooglePlay: 应用和游戏的内容分级

对于后台私信的开发者们,希望能够携带详细过审记录和拒审邮件一同发来,方便我们尽快解决问题 应用与游戏 为您的应用或游戏选择类别和标签选择要添加的标签选择类别并添加标签类别示例与应用、游戏以及两者中所投放广告的内容分级相关的要求应用如何获得内容分级内容分级的用…

将Notepad++添加到右键菜单【一招实现】

一键添加注册表 复制以下代码保存为 Notepad.reg&#xff0c;将红框内路径修改为自己电脑的“Notepad.exe路径”后&#xff0c;再双击运行即可。 Windows Registry Editor Version 5.00[HKEY_CLASSES_ROOT\*\shell\NotePad] "Notepad" "Icon""D:\\N…

[复健计划][紫书]Chapter 7 暴力求解法

7.1 简单枚举 例7-1 Division uva725 输入正整数n&#xff0c;按从小到大的顺序输出所有形如abcde/fghij n的表达式&#xff0c;其中a&#xff5e;j恰好为数字0&#xff5e;9的一个排列&#xff08;可以有前导0&#xff09;&#xff0c;2≤n≤79。枚举fghij&#xff0c;验证a…

【测试工具篇一】全网最强保姆级教程抓包工具Fiddler(2)

本文接上篇Fiddler介绍&#xff0c;开始讲fiddler如何使用之前&#xff0c;给大家讲讲http以及web方面的小知识&#xff0c;方便大家后面更好得理解fiddler使用。 目录 一、软件体系结构---B/S与C/S架构 B/S架构 C/S架构 二、HTTP基础知识 什么是http请求和响应? http协…

如何基于pdf2image实现pdf批量转换为图片

最近为了将pdf报告解析成为文本和图片&#xff0c;需要将大量多页的pdf文件拆分下单独的一页一页的图像&#xff0c;以便后续进行OCR和图像处理&#xff0c;因此就需要实现将pdf2image&#xff0c;本文主要结合开源的pdf2image和poppler&#xff0c;实现了pdf转换为png格式图片…

【Linux】Linux下查看cpu信息指令(top/mpstat/iostat/pidstat)说明

top命令 top(1) - Linux manual page (man7.org) top查看总的CPU利用率 us: 用户空间消耗的CPU资源占比&#xff0c;进程在用户态执行函数调用&#xff0c;编解码消耗的都是us sy: 内核空间消耗的CPU资源占比&#xff0c;进程调用系统调用达到内核后会增加sy的消耗 ni&…

Java学习者的福音:SpringBoot教学辅助平台

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理教学辅助平台的相关信息成为必然。开发合适…

csrf令牌

csrf get请求 路由 // index.php Route::get(/, function () {// return view(welcome);return view(login); });Route::get(d3,function(Request $request){echo "输入的内容是" . "<font color>".$request -> input(mytext)."</fon…

高校实验室安全巡检系统设计与实现(源码+定制+开发)高校实验室巡检系统、实验室安全管理平台、实验室安全监控系统、智能实验室巡查系统、高校实验室风险管理

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…