STM32CubeMX学习笔记(10)——SPI接口使用(读写SPIFlashW25Q64)

STM32CubeMX学习笔记(10)——SPI接口使用(读写SPIFlashW25Q64)


2023年12月1日发(作者:oppor9手机怎么样)

STM32CubeMX学习笔记(10——SPI接⼝使⽤(读写SPIFlashW25Q64

⼀、SPI简介

SPI(Serial Peripheral Interface) 协议是由摩托罗拉公司提出的通讯协议,即串⾏外围设备接⼝,是⼀种⾼速全双⼯的通信总线。

它被⼴泛地使⽤在 ADC、LCD 等设备与 MCU 间,要求通讯速率较⾼的场合。

芯⽚的管脚上只占⽤四根线。

MISO: 主器件数据输出,从器件数据输⼊。

MOSI:主器件数据输⼊,从器件数据输出。

SCK: 时钟信号,由主设备控制发出。

NSS(CS): 从设备选择信号,由主设备控制。当NSS为低电平则选中从器件。

⼆、引脚分布

STM32 芯⽚有多个 SPI 外设,它们的 SPI 通讯信号引出到不同的 GPIO 引脚上,使⽤时必须配置到这些指定的引脚。其中 SPI1 是

APB2 上的设备,最⾼通信速率达 36Mbtis/s,SPI2、SPI3 是 APB1 上的设备,最⾼通信速率为 18Mbits/s。除了通讯速率,在其它

功能上没有差异。

其中 SPI3 ⽤到了下载接⼝的引脚,这⼏个引脚默认功能是下载,第⼆功能才是 IO ⼝,如果想使⽤ SPI3 接⼝,则程序上必须先禁⽤掉这

⼏个 IO ⼝的下载功能。⼀般在资源不是⼗分紧张的情况下,这⼏个 IO ⼝是专门⽤于下载和调试程序,不会复⽤为 SPI3

三、FLASH芯⽚

开发板中的 FLASH 芯⽚型号:W25Q64。W25Q 系列为台湾华邦公司推出的是⼀种使⽤ SPI 通讯协议的 NOR FLASH 存储器。芯⽚型

主机⾸先通过 MOSI 线向 FLASH 芯⽚发送第⼀个字节数据为“9F h”,当 FLASH 芯⽚收到该数据后,它会解读成主机向它发送

1. 打开 STM32CubeMX 软件,点击“新建⼯程”

3. 配置时钟

4. 配置调试模式

⾮常重要的⼀步,否则会造成第⼀次烧录程序后续⽆法识别调试器

SYS 设置,选择 Debug 为 Serial Wire

五、SPI1

5.1 参数配置

中选择 设置,并选择 全双⼯主模式, 即不使⽤硬件⽚选信号

ConnectivitySPI1Full-Duplex Master不开启 NSS

原理图中虽然将 CS ⽚选接到了硬件 SPI1 的 NSS 引脚,因为硬件 NSS 使⽤⽐较⿇烦,所以后⾯直接把 PA4 配置为普通 GPIO,

⼿动控制⽚选信号。

在右边图中找到 SPI1 NSS 对应引脚,选择

GPIO_Output纠正:野⽕STM32F103指南者开发板SPI1 NSS须配置为PC0

修改输出⾼电平 ,标签为

HighW25Q64_CHIP_SELECT

SPI 为默认设置不作修改。只需注意⼀下, 分频系数最低为 ,波特率 (Baud Rate) 为 。这⾥被限制了,SPI1 最

Prescaler418.0 MBits/s

⾼通信速率可达 36Mbtis/s。

Clock Polarity(CPOL):SPI 通讯设备处于空闲状态时,SCK 信号线的电平信号(即 SPI 通讯开始前、 NSS 线为⾼电平时 SCK 的状

态)。CPOL=0 时, SCK 在空闲状态时为低电平,CPOL=1 时,则相反。

Clock Phase(CPHA):指数据的采样的时刻,当 CPHA=0 时,MOSI 或 MISO 数据线上的信号将会在 SCK 时钟线的“奇数边

沿”被采样。当 CPHA=1 时,数据线在 SCK 的“偶数边沿”采样。

根据 FLASH 芯⽚的说明,它⽀持 SPI ,⽀持双线全双⼯,使⽤ MSB 先⾏模式,数据帧长度为 8 位。

模式0模式 3

所以这⾥配置 CPOL 为 ,CPHA 为 即 SPI

Low1 Edge模式0

5.2 ⽣成代码

输⼊项⽬名和项⽬路径

选择应⽤的 IDE 开发环境 MDK-ARM V5

每个外设⽣成独⽴的 ⽂件

点击 GENERATE CODE ⽣成代码

5.3 封装SPI Flash(W25Q64)的命令和底层函数

向 SPI Flash 发送数据的函数

/**

* @brief SPI

发送指定长度的数据

* @param buf ——

发送数据缓冲区⾸地址

* @param size ——

要发送数据的字节数

* @retval HAL_OK

成功返回

*/

static HAL_StatusTypeDef SPI_Transmit(uint8_t* send_buf, uint16_t size)

{

return HAL_SPI_Transmit(&hspi1, send_buf, size, 100);

}

从 SPI Flash 接收数据的函数

/**

* @brief SPI

接收指定长度的数据

* @param buf ——

接收数据缓冲区⾸地址

* @param size ——

要接收数据的字节数

* @retval HAL_OK

成功返回

*/

static HAL_StatusTypeDef SPI_Receive(uint8_t* recv_buf, uint16_t size)

{

return HAL_SPI_Receive(&hspi1, recv_buf, size, 100);

}

发送数据的同时读取数据的函数

/**

* @brief SPI

在发送数据的同时接收指定长度的数据

* @param send_buf ——

接收数据缓冲区⾸地址

* @param recv_buf ——

接收数据缓冲区⾸地址

* @param size —— /

要发送接收数据的字节数

* @retval HAL_OK

成功返回

*/

static HAL_StatusTypeDef SPI_TransmitReceive(uint8_t* send_buf, uint8_t* recv_buf, uint16_t size)

{

return HAL_SPI_TransmitReceive(&hspi1, send_buf, recv_buf, size, 100);

}

5.4 编写W25Q64的驱动程序

5.4.1 读取 Manufacture ID Device ID

读取 Flash 内部这两个 ID 有两个作⽤:

检测 SPI Flash 是否存在

可以根据 ID 判断 Flash 具体型号

/**

* @brief FlashID

读取内部的

* @param none

* @retval device_id

成功返回

*/

uint16_t W25QXX_ReadID(void)

{

uint8_t recv_buf[2] = {0}; //recv_buf[0]Manufacture ID, recv_buf[1]Device ID

存放存放

uint16_t device_id = 0;

uint8_t send_data[4] = {ManufactDeviceID_CMD,0x00,0x00,0x00}; //+

待发送数据,命令地址

/* */

使能⽚选

HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_RESET);

/* */

发送并读取数据

if (HAL_OK == SPI_Transmit(send_data, 4))

{

if (HAL_OK == SPI_Receive(recv_buf, 2))

{

device_id = (recv_buf[0] << 8) | recv_buf[1];

}

}

/* */

取消⽚选

HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_SET);

return device_id;

}

5.4.2 读取状态寄存器数据并判断Flash是否忙碌

SPI Flash 的所有操作都是靠发送命令完成的,但是 Flash 接收到命令后,需要⼀段时间去执⾏该操作,这段时间内 Flash 处于“忙”状

态,MCU 发送的命令⽆效,不能执⾏,在 Flash 内部有 2-3 个状态寄存器,指⽰出 Flash 当前的状态,有趣的⼀点是:

当 Flash 内部在执⾏命令时,不能再执⾏ MCU 发来的命令,但是 MCU 可以⼀直读取状态寄存器,这下就很好办了,MCU可以⼀直读

/**

* @brief W25QXXW25Q642

读取的状态寄存器,⼀共有个状态寄存器

* @param reg —— (1~2)

状态寄存器编号

* @retval

状态寄存器的值

*/

static uint8_t W25QXX_ReadSR(uint8_t reg)

{

uint8_t result = 0;

uint8_t send_buf[4] = {0x00,0x00,0x00,0x00};

switch(reg)

{

case 1:

send_buf[0] = READ_STATU_REGISTER_1;

case 2:

send_buf[0] = READ_STATU_REGISTER_2;

case 0:

default:

send_buf[0] = READ_STATU_REGISTER_1;

}

/* */

使能⽚选

HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_RESET);

if (HAL_OK == SPI_Transmit(send_buf, 4))

{

if (HAL_OK == SPI_Receive(&result, 1))

{

HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_SET);

return result;

}

}

/* */

取消⽚选

HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_SET);

return 0;

}

然后编写阻塞判断 Flash 是否忙碌的函数:

/**

* @brief Flash

阻塞等待处于空闲状态

* @param none

* @retval none

*/

static void W25QXX_Wait_Busy(void)

{

while((W25QXX_ReadSR(1) & 0x01) == 0x01); // BUSY

等待位清空

}

5.4.3 读取数据

SPI Flash 读取数据可以任意地址(地址长度32bit)读任意长度数据(最⼤ 65535 Byte),没有任何限制。

/**

* @brief SPI FLASH

读取数据

* @param buffer ——

数据存储区

* @param start_addr —— (32bit)

开始读取的地址最⼤

* @param nbytes —— (65535)

要读取的字节数最⼤

* @retval 0-1

成功返回,失败返回

*/

int W25QXX_Read(uint8_t* buffer, uint32_t start_addr, uint16_t nbytes)

{

uint8_t cmd = READ_DATA_CMD;

start_addr = start_addr << 8;

W25QXX_Wait_Busy();

/* */

使能⽚选

HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_RESET);

SPI_Transmit(&cmd, 1);

if (HAL_OK == SPI_Transmit((uint8_t*)&start_addr, 3))

{

if (HAL_OK == SPI_Receive(buffer, nbytes))

{

HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_SET);

return 0;

}

}

HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_SET);

return -1;

}

5.4.4 写使能/禁⽌

Flash 芯⽚默认禁⽌写数据,所以在向 Flash 写数据之前,必须发送命令开启写使能。

/**

* @brief W25QXX,S1WEL

写使能寄存器的置位

* @param none

* @retval

*/

void W25QXX_Write_Enable(void)

{

uint8_t cmd= WRITE_ENABLE_CMD;

HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_RESET);

SPI_Transmit(&cmd, 1);

HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_SET);

W25QXX_Wait_Busy();

}

/**

* @brief W25QXX,WEL

写禁⽌清零

* @param none

* @retval none

*/

void W25QXX_Write_Disable(void)

{

uint8_t cmd = WRITE_DISABLE_CMD;

HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_RESET);

SPI_Transmit(&cmd, 1);

HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_SET);

W25QXX_Wait_Busy();

}

5.4.5 擦除扇区

SPI Flash有个特性:

数据位可以由1变为0,但是不能由0变为1。

所以在向 Flash 写数据之前,必须要先进⾏擦除操作,并且 Flash 最⼩只能擦除⼀个扇区,擦除之后该扇区所有的数据变为 (即全

0xFF

为1)。

/**

* @brief W25QXX

擦除⼀个扇区

* @param sector_addr ——

扇区地址根据实际容量设置

* @retval none

* @note

阻塞操作

*/

void W25QXX_Erase_Sector(uint32_t sector_addr)

{

uint8_t cmd = SECTOR_ERASE_CMD;

sector_addr *= 4096; //164KB

每个块有个扇区,每个扇区的⼤⼩是,需要换算为实际地址

sector_addr <<= 8;

W25QXX_Write_Enable(); //0xFF

擦除操作即写⼊,需要开启写使能

W25QXX_Wait_Busy(); //

等待写使能完成

/* */

使能⽚选

HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_RESET);

SPI_Transmit(&cmd, 1);

SPI_Transmit((uint8_t*)&sector_addr, 3);

HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_SET);

W25QXX_Wait_Busy(); //

等待扇区擦除完成

}

5.4.6 页写⼊操作

向 Flash 芯⽚写数据的时候,因为 Flash 内部的构造,可以按页写⼊。

/**

* @brief

页写⼊操作

* @param dat ——

要写⼊的数据缓冲区⾸地址

* @param WriteAddr ——

要写⼊的地址

* @param byte_to_write —— 0-256

要写⼊的字节数(

* @retval none

*/

void W25QXX_Page_Program(uint8_t* dat, uint32_t WriteAddr, uint16_t nbytes)

{

uint8_t cmd = PAGE_PROGRAM_CMD;

WriteAddr <<= 8;

W25QXX_Write_Enable();

/* */

使能⽚选

HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_RESET);

SPI_Transmit(&cmd, 1);

SPI_Transmit((uint8_t*)&WriteAddr, 3);

SPI_Transmit(dat, nbytes);

HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_SET);

W25QXX_Wait_Busy();

}

5.5 添加宏定义和全局变量

/* Private define ------------------------------------------------------------*/

/* USER CODE BEGIN PD */

#define ManufactDeviceID_CMD 0x90

#define READ_STATU_REGISTER_1 0x05

#define READ_STATU_REGISTER_2 0x35

#define READ_DATA_CMD 0x03

#define WRITE_ENABLE_CMD 0x06

#define WRITE_DISABLE_CMD 0x04

#define SECTOR_ERASE_CMD 0x20

#define CHIP_ERASE_CMD 0xc7

#define PAGE_PROGRAM_CMD 0x02

/* USER CODE END PD */

/* Private user code ---------------------------------------------------------*/

SPI_HandleTypeDef hspi1;

UART_HandleTypeDef huart1;

/* USER CODE BEGIN PV */

uint16_t device_id;

uint8_t read_buf[10] = {0};

uint8_t write_buf[10] = {0};

int i;

/* USER CODE END PV */

5.6 添加测试函数

/**

* @brief The application entry point.

* @retval int

*/

int main(void)

{

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/* USER CODE END 1 */

/* MCU Configuration--------------------------------------------------------*/

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */

HAL_Init();

/* USER CODE BEGIN Init */

/* USER CODE END Init */

/* Configure the system clock */

SystemClock_Config();

/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */

/* Initialize all configured peripherals */

MX_GPIO_Init();

MX_USART1_UART_Init();

MX_SPI1_Init();

/* USER CODE BEGIN 2 */

device_id = W25QXX_ReadID();

printf("W25Q64 Device ID is 0x%04xrn", device_id);

/* */

为了验证,⾸先读取要写⼊地址处的数据

printf("-------- read data before write -----------rn");

W25QXX_Read(read_buf, 0, 10);

for(i = 0; i < 10; i++)

{

printf("[0x%08x]:0x%02xrn", i, *(read_buf+i));

}

/* */

擦除该扇区

printf("-------- erase sector 0 -----------rn");

W25QXX_Erase_Sector(0);

/* */

再次读数据

printf("-------- read data after erase -----------rn");

W25QXX_Read(read_buf, 0, 10);

for(i = 0; i < 10; i++)

{

printf("[0x%08x]:0x%02xrn", i, *(read_buf+i));

}

/* */

写数据

printf("-------- write data -----------rn");

for(i = 0; i < 10; i++)

{

write_buf[i] = i;

}

W25QXX_Page_Program(write_buf, 0, 10);

/* */

再次读数据

printf("-------- read data after write -----------rn");

W25QXX_Read(read_buf, 0, 10);

for(i = 0; i < 10; i++)

{

printf("[0x%08x]:0x%02xrn", i, *(read_buf+i));

}

/* USER CODE END 2 */

/* Infinite loop */

/* USER CODE BEGIN WHILE */

/* USER CODE BEGIN WHILE */

while (1)

{

/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */

}

/* USER CODE END 3 */

}

5.7 查看打印

/**

* @brief GPIO Initialization Function

* @param None

* @retval None

*/

static void MX_GPIO_Init(void)

{

GPIO_InitTypeDef GPIO_InitStruct = {0};

/* GPIO Ports Clock Enable */

__HAL_RCC_GPIOC_CLK_ENABLE();

/*Configure GPIO pin Output Level */

HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_SET);

/*Configure GPIO pin : W25Q64_CHIP_SELECT_Pin */

GPIO_InitStruct.Pin = W25Q64_CHIP_SELECT_Pin;

GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

GPIO_InitStruct.Pull = GPIO_NOPULL;

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

HAL_GPIO_Init(W25Q64_CHIP_SELECT_GPIO_Port, &GPIO_InitStruct);

}

/**

* @brief SPI1 Initialization Function

* @param None

* @retval None

*/

static void MX_SPI1_Init(void)

{

/* USER CODE BEGIN SPI1_Init 0 */

/* USER CODE END SPI1_Init 0 */

/* USER CODE BEGIN SPI1_Init 1 */

/* USER CODE END SPI1_Init 1 */

/* SPI1 parameter configuration*/

hspi1.Instance = SPI1;

hspi1.Init.Mode = SPI_MODE_MASTER;

hspi1.Init.Direction = SPI_DIRECTION_2LINES;

hspi1.Init.DataSize = SPI_DATASIZE_8BIT;

hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;

hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;

hspi1.Init.NSS = SPI_NSS_SOFT;

hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;

hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;

hspi1.Init.TIMode = SPI_TIMODE_DISABLE;

hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;

hspi1.Init.CRCPolynomial = 10;

if (HAL_SPI_Init(&hspi1) != HAL_OK)

{

Error_Handler();

}

/* USER CODE BEGIN SPI1_Init 2 */

/* USER CODE END SPI1_Init 2 */

}

HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);

HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);

HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size,

uint32_t Timeout);

使⽤ STM32 标准库的代码:

/**

* @brief SPI_FLASH

初始化

* @param

* @retval

*/

void SPI_FLASH_Init(void)

{

SPI_InitTypeDef SPI_InitStructure;

GPIO_InitTypeDef GPIO_InitStructure;

/* SPI */

使能时钟

FLASH_SPI_APBxClock_FUN ( FLASH_SPI_CLK, ENABLE );

/* SPI */

使能引脚相关的时钟

FLASH_SPI_CS_APBxClock_FUN (FLASH_SPI_CS_CLK|FLASH_SPI_SCK_CLK|

FLASH_SPI_MISO_PIN|FLASH_SPI_MOSI_PIN, ENABLE );

/* SPI CSIO */

配置引脚,普通即可

GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure);

/* SPI SCK*/

配置引脚

GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);

/* SPI MISO*/

配置引脚

GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;

GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure);

/* SPI MOSI*/

配置引脚

GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;

GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure);

/* FLASH: CS*/

停⽌信号引脚⾼电平

SPI_FLASH_CS_HIGH();

/* SPI */

模式配置

// FLASH SPI03CPOL CPHA

芯⽚⽀持模式及模式,据此设置

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;

SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;

SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;

SPI_InitStructure.SPI_CRCPolynomial = 7;

SPI_Init(FLASH_SPIx , &SPI_InitStructure);

/* SPI */

使能

SPI_Cmd(FLASH_SPIx , ENABLE);

}

void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);

void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);

uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);

六、注意事项

⽤户代码要加在 之间,否则下次使⽤ STM32CubeMX 重新⽣成代码后,会被删除。

USER CODE BEGIN NUSER CODE END N

· 由 写于 2021 年 1 ⽉ 27 ⽇

· 参考:


发布者:admin,转转请注明出处:http://www.yc00.com/num/1701382058a1075485.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信