基于STM32CubeMX和keil采用SPI通信实现轮询方式读写W25Q128获取设备ID

news2024/7/2 4:10:42

文章目录

  • 前言
  • 1. SPI通信
    • 1.1 SPI硬件接口与连线
    • 1.2 SPI传输协议
    • 1.3 SPI的数据传输
  • 2. W25Q128
    • 2.1 片选
    • 2.2 控制指令
    • 2.3 时序图分析
  • 3. STM32CubeMX配置
    • 3.1 引脚配置
    • 3.2 时钟配置
    • 3.3 SPI配置
    • 3.4 工程配置
  • 4. 代码编写
    • 4.1读ID函数
  • 总结

前言

最近使用通信比较多,包含UART,I2C,SPI,DMA等。这篇博客主要是对近期项目使用的SPI进行讲解,并结合普中STM32F407ZET6芯片以及W25Q128芯片和所对应模块电路进行阐述。
主要是对SPI通信的基本原理和流程,W325Q128的原理图以及芯片手册的阅读进行阐述。


1. SPI通信

1.1 SPI硬件接口与连线

SPI通信一般会用到四个接口,它们分别是:MOSI,MISO,CLK,NSS
MOSI主输出,从输入Master Output Slave Input ,主设备输出/从设备输入信号,从设备上该信号一般简写为SI。MOSI是主设备的串行数据输出,SI是从设备的串行数据输入,主设备和从设备的这两个信号连接。

MISO主输入,从输出。Master Input Slave Output,主设备输入/从设备输出信号,从设备上该信号一般简写为SO。MISO是主设备的串行数据输入,SO是从设备的串行数据输出,主设备和从设备的这两个信号链接。
CLK时钟。时钟在SPI通信中是通过主机给予的,从机是没有CLK的。
NSS片选。SPI通信中,在一主多从的情况下,一般来说只有片选引脚置低,对应的从机才有效。部分芯片是高电平有效。

一主多从情况的连线图大致如下:

在这里插入图片描述

1.2 SPI传输协议

SPI通信在CLK的驱动下进行串行传输,SPI的传输协议定义了SPI的起始信号,结束信号,数据有效性,时钟同步。
SPI每次传输的数据帧长度是8位或者16位。
SPI通信有四种时序模式。四种时序模式和CPOL,CPHA这两个参数有关。前者是时钟极性(Clock Polarity),后者是时钟相位(Clock Phase)。

CPOL:时钟极性。时钟在空闲状态时的电平。如果CPOL=0,则空闲时,CLK为低电平;如果CPOL=1,则空闲时,CLK为高电平。
CPHA:时钟相位。如果CPHA=0,则在SCK的第一个边沿对数据采样;如果CPHA-1,则在SCK的第2个边沿数据采样。

所有四种时序模式就可以如下表所示:

时序模式CPOLCPHA说明
000时钟空闲为low,对SCK的第1个跳变边沿数据采样
101时钟空闲为low,对SCK的第2个跳变边沿数据采样
210时钟空闲为high,对SCK的第1个跳变边沿数据采样
311时钟空闲为high,对SCK的第2个跳变边沿数据采样

下图为CPHA=0,CPOL=1和CPOL=0情况对应的时序图。看懂这个应该就知道SPI的原理了。
在这里插入图片描述

使用SPI通信要特别注意,主从机设备的SPI时序一定要一致。 其实这就类似于主从机数据解析方式,如果我主机按时序模式0发,从机按时序模式3收,那这个从机收到的数据就和主机发送的数据不一致了。这个要特别注意。


1.3 SPI的数据传输

SPI传输数据的方式分为三种,阻塞式,中断式,DMA方式。
下面分别简单介绍一下,这篇博客就用最简单的阻塞式发送进行验证。
1.阻塞式
一般阻塞式发送用到的函数为:HAL_SPI_Transmit(),这个函数表示阻塞式发送一个缓冲区的数据。
完整的定义如下:
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)

一般阻塞式接收用到的函数为:HAL_SPI_Receive(),这个函数表示阻塞式接收指定长度的数据保存到缓冲区。
完整的定义如下:
HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)

发送与接受函数的四个形参含义:

hspi是SPI的外设对象指针,表明用的是哪一个SPI的指针。
pData表示发送/接收的数据缓冲区的指针
Size表示发送/接收的缓冲区数据的长度
Timeout表示超时等待的时间,单位是系统嘀嗒信号节拍数,默认情况下就是ms

除了上面的单独发送与接受的函数,还有一个集成收发的函数HAL_SPI_TransmitReceive(),这个函数表示阻塞式同时发送和接受一定长度的数据。
完整的定义如下:
HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size,uint32_t Timeout)

这个函数的几个参数含义和上面类似,不过是把收发数据的指针进行了分开,也就是把一个收发指针变成了发送指针与接收指针。
将pData分为pTxData与pRxData,其他的参数我这里就不再解释。


2.中断式
和上面阻塞式对应,中断式发送与接收数据有三个函数,它们分别是:HAL_SPI_Transmit_IT(),HAL_SPI_Receive_IT(),HAL_SPI_TransmitReceive_IT()
完整定义如下:

HAL_StatusTypeDef HAL_SPI_Transmit_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)
HAL_StatusTypeDef HAL_SPI_Receive_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)
HAL_StatusTypeDef HAL_SPI_TransmitReceive_IT(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size)

我们类比阻塞式发送,发现其实它们的参数都是差不多的,只不过少了个超时参数。所以共性的部分我就不解释了。不同的地方在于,函数名字后面多了一个IT,这表示Interrupt(中断)。
这三个函数都是表示以中断方式发送/接受数据,它们执行完成后都会产生中断事件。
中断发送方式会产生SPI_IT_TXE,会调用回调函数:HAL_SPI_TxCpltCallback()
中断接收方式会产生SPI_IT_RXNE,会调用回调函数:HAL_SPI_RxCpltCallback()
中断发送接收方式会产生SPI_IT_TXE、SPI_IT_RXNE,会调用回调函数:HAL_SPI_TxRxCpltCallback()

关于回调函数之类的,其实和我写外部中断篇博客类似,这里也不再阐述。


3.DMA式
DMA发送与接收数据有三个函数,
它们分别是:HAL_SPI_Transmit_DMA(),HAL_SPI_Receive_DMA(),HAL_SPI_TransmitReceive_DMA()
完整定义如下:

HAL_StatusTypeDef HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)
HAL_StatusTypeDef HAL_SPI_Receive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)
HAL_StatusTypeDef HAL_SPI_TransmitReceive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size)

DMA也会产生中断,只不过中断有两种,分为传输完成和传输半完成。

DMA方式功能函数函数功能DMA流中断事件对应回调函数
HAL_SPI_Transmit_DMADMA方式发送数据DMA传输完成/DMA传输半完成HAL_SPI_TxCpltCallback()/HAL_SPI_TxHalfCpltCallback()
HAL_SPI_Receive_DMADMA方式发送数据DMA传输完成/DMA传输半完成HAL_SPI_TxCpltCallback()/HAL_SPI_TxHalfCpltCallback()
HAL_SPI_TransmitReceive_DMADMA方式发送/接收数据DMA传输完成/DMA传输半完成HAL_SPI_TxRxCpltCallback()/HAL_SPI_TxRxHalfCpltCallback()

2. W25Q128

看了好多STM32的开发板,都是用这个芯片作为SPI通信的示例芯片来做。SPI通信的基本配置以及原理并不难,刚开始学的时候觉得难大概率和使用的芯片进行通讯需要看芯片手册难,这个小节就写一下使用芯片进行SPI通信的部分,详细的芯片手册阅读我后面写一篇贴上来。

2.1 片选

我们SPI有数据传输引脚,时钟,还有片选。
一般信号片选信号拉低为有效,这个可以根据芯片手册中的介绍以及时序图看出。
下图是普中F407ZET6开发板W25Q128部分的原理图,我们可以看到片选/CS是低电平有效。上面有一杠的标志,就表示是低电平有效。

在这里插入图片描述
下面是摘自芯片手册中关于片选的基本介绍。
在这里插入图片描述

关键的部分人话翻译一下:
/CS拉高,表示设备未选中,串行输出脚是高阻态。未选中的时候,芯片啥也不干的时候处于待机状态,除非进行内部擦除、编程或写入状态寄存器循环。
/CS拉低,设备选中。芯片激活状态,程序员可以写控制指令和数据进去。

说白了就是拉低有效,拉高无效。


2.2 控制指令

这篇博客的目的是获取设备ID,所以我们在确定片选拉低之后要找怎么获取设备ID。
查阅数据手册,找到了下图部分信息。
这个表告诉我们我们使用Command指令90h也就是向芯片输入控制字0x90h,后面的dummy可不是笨蛋的意思哈,在电子行业这个是虚设的意思,也就是无效单位,只是为了填充或满足某些要求或约束,我们一般给00h就行了。
在这里插入图片描述

下面这里给提示,我们给完获取设备ID指令后,它会一直发送,直到片选拉高。
在这里插入图片描述

写到这里,其实还是云里雾里有点。我为什么发这个指令,芯片就给我发ID数据呢?我怎么知道什么时候芯片发,什么时候我发数据?这就和时序图有关系了。现在来分析一下时序图。

2.3 时序图分析

在这里插入图片描述

上面这个图是连着的,只不过由于页面宽度的原因它分层了,为了便于查看,我截图稍微整理了一下,这样看起来更加完整。
在这里插入图片描述

我们这里是SPI通信,先看片选,再看时钟来确定模式,再看数据传输方向。

首先是片选拉低。我们可以看到在片选拉低的时候才会有数据传输部分,一旦拉高,后面的数据传输都存在了。这说明片选拉低有效(说了很多次了,有点啰嗦)

其次看时钟。这个时序图中CLK有Mode3与Mode0模式,对应的CPOL和CPHA都为0和都为1情况。具体对应关系刚写过了,忘了的话往上翻翻。这个模式主从机要一致,这里W25Q128只支持模式0和模式3,所以芯片手册的时序图里面将它的时钟效果列了出来。
我们可以看到前8个时钟周期是控制指令周期,后面从第九个周期(数字是8,从0开始的)到第32个周期是写地址周期。后面的时钟周期一直到/CS拉高,其实都和主机输入没啥关系了,时序图DI后面都是XXX了,无效数据。

再看数据传输。主机向芯片输入指令的时候,我们看DI线,是数据有效的,DO线高阻态。结合时钟就是前32个时钟周期主机数据过来有效,后面无效。33时钟周期开始,DI的数据都变成了XXXX,也就是无效,这时候DO线的数据有效,表示芯片向主机发送数据。且一直发送,直到片选拉高。
这里不间断传输数据其实芯片手册也有介绍,xxxx can be read continuously。
同时如果我们地址为部分给01,输出的先是设备ID再制造商ID,这里就不过多讨论。

在这里插入图片描述


3. STM32CubeMX配置

3.1 引脚配置

看一下原理图的接线
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

结合原理图,我们进行配置。

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

3.2 时钟配置

开外部晶振,之前我写过,这个不一定要开,只要你想要的频率满足需要的要求,且能通过内部晶振分频获得就可以不开启外部晶振。我一般都是习惯性开启,也不会影响什么。
在这里插入图片描述SPI1在APB2总线上,所以SPI通信的波特率其实是根据PCLK2的频率来算的。
在这里插入图片描述

3.3 SPI配置

在这里插入图片描述

这里个字段含义解释一下:
Mode:工作模式。STM32作为主机时,一般选权全双工主机,做从机全双工从机。全双工是指使用MISO和MOSI线可以同时接收和发送。还有半双工模式,就是只用一根数据线,可发又可收,但是同一时间只能有一种功能。
Hardware NSS Signal:硬件NSS信号。三种选项,Disable表示不使用NSS硬件信号;Hardware Nss Input Signal 表示硬件NSS输入信号,SPI 从机使用硬件NSS信号时选择此项。Hardware Nss output Signal示硬件NSS输出信号,SPI主机输出片选时选择这个,但是我们是使用单独的GPIO作为从机的片选信号,所以这里设置为Disable。
Frame Format:帧格式。有Motorola和TI两个选项。但一般只能选Motorola,这个参数对应控制寄存器SPI_CR2的FRF位。
Data Size:数据大小。数据帧的位数,可选8/16bits。
First Bit:首选传输的位。可选MSB First或LSB First。
Prescaler(for Baud Rate):用于产生波特率的预分频系数。就是拿那个SPI对应的APBx的总线频率除以这个分频系数,就是波特率了。这里是APB2所以是50MHz再除以8分频,所以波特率就是6.25MBits/s。
CPOL和CPHA我就不解释了,一开始讲过了。
CRC Calculation:CRC(循环冗余校验)。这个主要是传输数据的最后加上一个字节的CRC计算结果,在发生CRC错误的时候可以产生中断。不使用就默认为Disabled
NSS Signal Type:NSS信号类型。这个参数和上面的Hardware NSS Signal的选择结果决定。当Hardware NSS Signal 为disable时,这个参数的选项就只能是Software,表示用软件产生NSS输出信号,即本示例用PB14输出信号作为从机的片选信号。


3.4 工程配置

工程配置因人而异,这里就给个参考

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


4. 代码编写

4.1读ID函数

先贴代码,再详细解释。

uint16_t Flash_ReadIDbywzy(void)
{
	uint16_t Temp = 0;
	uint8_t	byteData=0;
	uint8_t commandData=0x90;
	HAL_GPIO_WritePin(FLASH_CS_GPIO_Port, FLASH_CS_Pin, GPIO_PIN_RESET);//cs=0
	HAL_SPI_Transmit(&hspi1, &commandData, 1, 200);
	commandData=0x00;
	HAL_SPI_Transmit(&hspi1, &commandData, 1, 200);
	HAL_SPI_Transmit(&hspi1, &commandData, 1, 200);
	HAL_SPI_Transmit(&hspi1, &commandData, 1, 200);
	
	HAL_SPI_Receive(&hspi1, &byteData, 1, 200);
	Temp=byteData;
	Temp=Temp<<8;//Manufacturer ID
	
	HAL_SPI_Receive(&hspi1, &byteData, 1, 200);
	Temp|=byteData;	//Device ID
	
	HAL_GPIO_WritePin(FLASH_CS_GPIO_Port, FLASH_CS_Pin, GPIO_PIN_SET);

	return Temp;
}

代码的流程:
首先定义一些接收和发送数据的变量,接着SPI通信流程,片选使能,发送指令,这里发送的是0x90000000,接着开始读数据,一次读一个字节数据,读两次,读完数据再拉高片选结束一次传输,并把接收到的数据返回。
在这里插入图片描述

代码验证的话,如果手头没有啥工具(比如USB转TTL,屏幕这种),那就打断点调试看数据吧。
这里断点到第三个的时候,看flashID的变量就可以看到了。其中前两位是Manufacturer ID,后两位是Device ID。
在这里插入图片描述
在这里插入图片描述这里单步调试可能有点BUG,第一次执行完break出来,这个flashID的值为FFFF,然后按RST之后再运行到break的断点,它就会有正确的ID了。

这里把核心代码贴了,还有一些细节这里就不写了,比如定义函数,头文件添加,烧录配置什么的,重复性的东西之前都写过,这里就不再赘述了,同样的东西多写无意义。
完整代码以及项目工程我传到博客对应的资源里面。有需要自取。


总结

本篇博客从SPI通信原理到W25Q128芯片手册阅读,时序图阅读,再到STM32CubeMX配置和代码编写,整个项目工程非常完善,形成闭环。写的也非常详细,通俗不通俗我不知道,我觉得还是挺好懂的。
这篇博客写起来确实非常非常费时间,好在收获颇丰。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

多仓库手机端erp进销存pc/h5开源版开发

多仓库手机端erp进销存pc/h5开源版开发 以下是多仓库手机端ERP进销存PC/H5的功能列表&#xff1a; 仓库管理&#xff1a;包括仓库的新增、编辑、删除、查询等功能&#xff0c;可以管理多个仓库的库存情况。 商品管理&#xff1a;可以对商品进行新增、编辑、删除、查询等操作&a…

【JavaSE】面向对象之多态

文章目录 多态的概念多态实现条件重写向上转型和向下转型向上转型向下转型 避免在构造方法中调用重写的方法 多态的概念 通俗来说&#xff0c;就是多种形态&#xff0c;具体点就是去完成某个行为&#xff0c;当不同的对象去完成时会产生出不同的状态。 多态实现条件 必须要…

通过LD_PRELOAD绕过disable_functions

LD_PRELOAD 在UNIX的动态链接库的世界中&#xff0c;LD_PRELOAD就是这样一个环境变量&#xff0c;它可以影响程序的运行时的链接&#xff08;Runtime linker&#xff09;&#xff0c;它允许你定义在程序运行前优先加载的动态链接库。这个功能主要就是用来有选择性的载入不同动态…

PANGOLIN写字

效果: 代码: #include <iostream> #include <pangolin/pangolin.h>// pangolin 绘制文字demousing namespace std;int main() {//创建一个窗口pangolin::CreateWindowAndBind("PangolinShowText", 640, 480);// 定义字体pangolin::GlFont * text_font …

⛳ TCP 协议详解

目录 ⛳ TCP 协议详解&#x1f3a8; 一、TCP / IP 协议的分层模型&#x1f3d3; 1.1、应用层&#x1f9f8; 1.2、传输层&#x1f52e; 1.3、网络层&#x1f3af; 1.4、链路层 &#x1f3ed; 二、HTTP 报文传输原理&#x1f945; 2.1、HTTP 报文传输&#x1f48e; 2.2、封装和分…

【Linux取经路】解析环境变量,提升系统控制力

文章目录 一、进程优先级1.1 什么是优先级&#xff1f;1.2 为什么会有优先级&#xff1f;1.3 小结 二、Linux系统中的优先级2.1 查看进程优先级2.2 PRI and NI2.3 修改进程优先级2.4 进程优先级的实现原理2.5 一些名词解释 三、环境变量3.1 基本概念3.2 PATH&#xff1a;Linux系…

操作系统练习:在Linux上创建进程,及查看进程状态

说明 进程在执行过程中可以创建多个新的进程。创建进程称为“父进程”&#xff0c;新的进程称为“子进程”。每个新的进程可以再创建其他进程&#xff0c;从而形成进程树。 每个进程都有一个唯一的进程标识符&#xff08;process identifier&#xff0c;pid&#xff09;。在L…

games101-windows环境配置(CMake+vcpkg+VS2019)

下载工具 安装CMake 安装vcpkg 安装vs2019 安装 eigen3 opencv 在vcpkg安装目录下&#xff0c;使用Windows Power Shell运行下面脚本 .\vcpkg.exe install eigen3:x64-windows .\vcpkg.exe install opencv:x64-windows安装过程中可能会用红色字体提示&#xff1a;Failed to…

七夕前的爱心代码!

话不多说上代码&#xff01; import turtle as tu import random as ratu.setup(1.0, 1.0) tu.screensize(1.0, 1.0) tu.bgcolor(black) t tu.Pen() t.ht() colors [pink, hotpink, deeppink, lightpink, red, purple, violet, magenta]def draw_star(x, y, size, color):t.…

二、Kafka快速入门

目录 2.1 安装部署1、【单机部署】2、【集群部署】 2.2 Kafka命令行操作1、查看topic相关命令参数2、查看当前kafka服务器中的所有Topic3、创建 first topic4、查看 first 主题的详情5、修改分区数&#xff08;注意&#xff1a;分区数只能增加&#xff0c;不能减少&#xff09;…

msvcr120.dll丢失有哪些简单的恢复方法?

在日常使用计算机的过程中&#xff0c;我们可能会遇到各种问题&#xff0c;比如系统崩溃、程序无法运行等。最近&#xff0c;我在使用一些软件时遇到了一个问题&#xff1a;程序无法正常运行&#xff0c;提示找不到msvcr120.dll文件。经过一番查找和尝试&#xff0c;我终于找到…

查询投稿期刊的好用网址

网址 搞科研&#xff1a;查询SCI期刊的ISSN、期刊名称、大类分区、影响因子 http://www.gaokeyan.com/journal/index.php Letpub&#xff1a;查询期刊详细信息 https://www.letpub.com.cn/index.php?pagejournalapp 小木虫&#xff1a;查询期刊的评价 http://muchong.com/…

基于小程序的汽车俱乐部系统的设计与实现(论文+源码)_kaic

目录 前 言 1 系统概述 1.1 系统主要功能 1.2 开发及运行环境 2 系统分析和总体设计 2.1 需求分析 2.2 可行性分析 2.3 设计目标 2.4 项目规划 2.5 系统开发语言简介 2.6 系统功能模块图 3 系统数据库设计 3.1 数据库开发工具简介 3.2 数据库需求分析 3.3 数据库…

防火墙firewall

一、什么是防火墙 二、iptables 1、iptables介绍 2、实验 138的已经被拒绝&#xff0c;1可以 三、firewalld 1、firewalld简介 关闭iptables&#xff0c;开启firewalld&#xff0c;curl不能使用&#xff0c;远程连接ssh可以使用 添加80端口 这样写也可以&#xff1a;添加http…

[NLP] BERT模型参数量

一 BERT_Base 110M参数拆解 BERT_base模型的110M的参数具体是如何组成的呢&#xff0c;我们一起来计算一下&#xff1a; 刚好也能更深入地了解一下Transformer Encoder模型的架构细节。 借助transformers模块查看一下模型的架构&#xff1a; import torch from transformers …

Leetcode.118 杨辉三角

题目链接 Leetcode.118 杨辉三角 easy 题目描述 给定一个非负整数 n u m R o w s numRows numRows&#xff0c;生成「杨辉三角」的前 n u m R o w s numRows numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows 5 输出:…

STM32--DMA

文章目录 DMA简介DMA特性 DMA框图DMA基本结构DMA请求数据宽度对齐DMA数据转运工程DMAADC多通道 DMA简介 直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预&#xff0c;数据可以通过DMA快速地移动&#xff0c;这就节省了CPU的…

Goland 注释时自动在注释符号后添加空格

不得不说 JetBrains 旗下的 IDE 都好用&#xff0c;而且对于注释这块&#xff0c;使用 Ctrl / 进行注释的时候&#xff0c;大多会在每个注释符号后统一添加一个空格&#xff0c;比如 PyCharm 和 RubeMine 等。 # PyCharm # print("hello world") # RubyMine # req…

基于web网上订餐系统的设计与实现(论文+源码)_kaic

目录 1绪论 1.1课题研究背景 1.2研究现状 1.3主要内容 1.4本文结构 2网上订餐系统需求分析 2.1系统业务流程分析 2.2消费者用户业务流程分析 2.3商户业务流程分析 2.4管理员用户流程分析消费者用户用例分析 2.5系统用例分析 3网上订餐系统设计 3.1功能概述 3.2订单管理模块概要…

MySQL安装、配置和启动关闭

1. 概述 本文主要内容&#xff1a; MySQL下载&#xff1b;MySQL的安装&#xff1b;配置环境变量&#xff1b;登录MySQL服务器&#xff1b;查询系统数据库&#xff1b;启动和关闭服务&#xff1b; 2. 安装、配置、启动与关闭服务 2.1. MySQL下载 在MySQL官网就可以下载。 …