STM32的UART串口通信协议

您所在的位置:网站首页 stm32串口单线半双工 STM32的UART串口通信协议

STM32的UART串口通信协议

2023-11-02 14:32| 来源: 网络整理| 查看: 265

文章参考:  [基于STM32之UART串口通信协议(一)详解](https://www.cnblogs.com/ChurF-Lin/p/10793111.html)、[STM32入门教程(基于HAL库+CubeMX+MDK-ARM)](https://www.bilibili.com/video/BV1y7411m7gg?p=78)、[一文看懂stm32的引脚的两种用途:GPIO和AFIO](http://m.elecfans.com/article/677574.html)[、STM32端口复用和重映射](https://blog.csdn.net/qq_34692743/article/details/80495858)、[stm32端口复用和重映射](https://blog.csdn.net/williamgavin/article/details/79776654?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromBaidu-1.add_param_isCf&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromBaidu-1.add_param_isCf) 硬件:STM32F107VC( Cortex-M3) 软件:Keil μVision4,JLINK 一、计算机通信的基本概念        将计算机技术和通信技术相结合,完成计算机与外部设备或计算机与计算机之间的信息交换,按照**数据传输方式**的不同,可分为**并行通信**和**串行通信**。串行通信按照数据同步的方式,又可分为**同步串行**和**异步串行**两种。

串口通信的数据传输方向:

① 单工方式:当进行数据传送时,只用一根数据线且只能进行单向传输的方式

② 半双工方式:当进行数据传送时,只用一根数据线利用不同时段进行双向传输的方式

③ 全双工方式:当进行数据传送时,利用两根数据线同时进行发送和接收的方式,典型实例:UART。

1. 并行通信:

     多位数据同时传输,传输控制简单,传输速度快,但是在长距离传输时硬件成本较高。

2. 串行通信:

 数据逐位传输,传输线少,长距离传输时成本低,但数据的传输控制较复杂。

(1)同步串行通信

      数据传输以数据块(一组字符)为单位,在一个数据块内,字符与字符间无间隔,收发双方依靠独立的时钟线进行信号的同步。由主机提供时钟与数据,从机使用该时钟接收数据或发送数据,即通信双方公用时钟源。例如:SPI和I2C就属于串行同步通信。适用于大批量的数据传输。

(2)异步串行通信

       数据传输以单个字符为单位,字符与字符之间的间隙任意,字符内部每一位的持续的时间相同。收发双方不使用公共的参考时钟,通信双方分别有自己的时钟源。收发双方依靠事先约定的字符格式和通信速率来完成通信。例如USART就是属于串行异步通信。

**   A. 异步串行通信的字符格式:**

一个字符帧包括:

* 起始位:

      当未有数据发送时,数据线处于逻辑“1”状态;先发出一个逻辑“0”信号,表示开始传输字符。 * 数据位:

     紧接着起始位之后。资料位的个数可以是4、5、6、7、8等,构成一个字符。通常采用ASCII码。从最低位开始传送,靠时钟定位。

奇偶校验位:

       资料位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验资料传送的正确性。

停止位:

      一个字符数据的结束标志。可以是0.5位、1位、1.5位、2位的高电平。 由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。

     注意: 空闲位:处于逻辑“1”状态,表示当前线路上没有资料传送,进入空闲状态。处于逻辑“0”状态,表示开始传送下一数据段。

     常用字符格式:1位起始位,8位数据位,无奇偶校验,1位停止位。

B. 异步串行通信的通信速率:

      波特率:每秒钟传送二进制数码的位数,以bit/s(bps)为单位。是衡量数据传送速率的指标,它用单位时间内载波调制状态改变的次数来表示。

     常用的波特率有:9600、19200、38400、57600和115200。

     时间间隔计算:1秒除以波特率得出的时间,例如,波特率为115200,表示每秒传输115200位,且每一位在数据线上持续实践,即时间间隔为Tbit=1s/ 115200 = 8.68us。

C. 异步串行通信的通信准确性

异步串行通信的两个关键点:字符格式,决定了字符中数据的传输方式;波特率,决定了字符中每一位数据的持续时间。

D. 异步串行通信的数据接收过程

接收过程的本质是数据采集,假设接收端的采样时钟是波特率的16倍。

① 接收过程由起始位的下降沿启动;

② 接收端等待8个时钟周期,以便建立一个接近比特周期中间的采样点;

③ 接收端等待16个时钟周期,使其进入第一个数据位周期的中点;

④ 第一个数据位被采样被存储在接收寄存器中;

⑤ 串口模块在采样第二个数据位之前等待另外16个时钟周期;

⑥ 重复此过程,直到所有数据位都被采样和存储;

⑦ 由停止位的上升沿使数据线返回到空闲状态。

二、UART详解 1. UART简介

       嵌入式开发中,UART串口通信协议是我们常用的通信协议(UART、I2C、SPI等)之一,全称叫做通用异步收/发传输器(Universal Asynchronous Receiver/Transmitter),是异步串口通信协议的一种,工作原理是将传输数据的每个字符一位接一位地传输,它能将要传输的资料在串行通信与并行通信之间加以转换,能够灵活地与外部设备进行全双工数据交换。

      在此开发板中,是有USART(Universal Synchronous Asynchronous Receiver and Transmitter通用同步异步收发器)串口的,USART相当于UART的升级版,USART支持同步模式,因此USART 需要同步始终信号USART_CK(如STM32 单片机),通常情况同步信号很少使用,因此一般的单片机UART和USART使用方式是一样的,都使用异步模式。因为USART的使用方法上跟UART基本相同,所以在此就以UART来讲该通信协议了。

     USART主要特性:

全双工的,异步通信 NRZ标准格式  分数波特率发生器系统  可编程数据字长度(8位或9位)  可配置的停止位-支持1或2个停止位  单线半双工通信    可配置的使用DMA的多缓冲器通信 10个带标志的中断源 四个错误检测标志 多 处理器通信 -- 如果地址不匹配,则进入静默模式 从静默模式中唤醒(通过空闲总线检测或地址标志检测)  两种唤醒接收器的方式:地址位(MSB,第9位),总线空闲 2. USART串口结构

       串口收发单元主要利用发送数据寄存器TDR、接收数据寄存器RDR和发送数据引脚输出TX、接收数据输入引脚RX,以及三个通信状态位TXE、TC、RXNE来完成数据的接收和发送。

A.  引脚

  **RX:**接收数据输入引脚就是串行数据输入引脚。通过过采样技术来区分有效输入数据和噪声,从而用于恢复数据。 **TX:**发送数据输出引脚。如果关闭发送器,该输出引脚模式由其 I/O 端口配置决定。如果使能了发送器但没有待发送的数据,则 TX 引脚处于高电平。在单线和智能卡模式下,该 I/O 用于发送和接收数据(USART 电平下,随后在 SW_RX 上接收数据)。

注意:

SCLK:发送器时钟输出引脚,仅适用于同步模式。该引脚用于输出发送器数据时钟,以便按照 SPI 主模式进行同步发送(起始位和结束位上无时钟脉冲,可通过软件向最后一个数据位发送时钟脉冲)。RX 上可同步接收并行数据。这一点可用于控制带移位寄存器的外设(如 LCD 驱动器)。时钟相位和极性可通过软件编程。在智能卡模式下,SCLK 可向智能卡提供时钟。

在硬件流控制模式下需要以下引脚:

nCTS:“清除以发送”,n表示低电平有效。如果使能CTS流控制,发送器在发送下一帧数据之前会检测nCTS引脚,如果为低电平,表示可以发送数据,如果为高电平,则在发送完当前数据帧之后停止发送。 nRTS:“请求以发送”,n表示低电平有效。如果使能RTS流控制,当USART接收器准备好接收新数据时就会将nRTS 变成低电平;当接收器已满时,nRTS 将被设置为高电平。

B. 通信状态位

       在轮询方式下可以直接检测标志位;在中断方式下,需要中断服务程序中通过检测不同的中断标志位,来判断中断类型,然后执行后续的任务处理。

3. USART 工作原理

       串口USART是APB1总线上的外设单元,通过波特率寄存器USART_BRR和串口控制寄存器USART_CRx(x=1,2,3)配置串口的波特率和工作模式。

       通过数据的流向进行区分,在结构设计上采用了双缓冲结构,数据收发过程中,可同时写入新的数据或读取已接受的数据,提高数据的传输效率。

      发送时,数据通过数据总线送入发送数据寄存器TDR,可按设定的波特率实现数据的发送,然后传送到发送移位寄存器完成数据转换,从并行数据转为串行输出,最后通过TX引脚发送。

      接受时,数据通过RX引脚逐位送入接收移位寄存器,8位数据接收完成后,送入接收数据寄存器RDR,供用户读取,APB1总线读RDR寄存器可读到串口接收的数据。

      串口的数据发送和接收状态保存在串口状态寄存器中,一般地,串口发送数据通过写TDR寄存器实现,而串口接收数据通过串口中断实现。

      USART 支持 DMA 传输,可以实现高速数据传输。 

4. USART的应用

     USART直接和其他的控制器进行数据交换;

     USART和PC机通信,由于PC机串口是RS232电平,所以连接时需要使用RS232转换器 。

     在开发板上,默认使用UART2和PC通信。使用的引脚如下图:

三、USART的编程 1. 配置USART参数

    下面从库函数的层面来说明如何初始化IO口。在stm32f10x_usart.h里定义了 USART_InitTypeDef结构体变量。

typedef struct { uint32_t USART_BaudRate; /*!< This member configures the USART communication baud rate. The baud rate is computed using the following formula: - IntegerDivider = ((PCLKx) / (16 * (USART_InitStruct->USART_BaudRate))) - FractionalDivider = ((IntegerDivider - ((uint32_t) IntegerDivider)) * 16) + 0.5 */ uint16_t USART_WordLength; /*!< Specifies the number of data bits transmitted or received in a frame. This parameter can be a value of @ref USART_Word_Length */ uint16_t USART_StopBits; /*!< Specifies the number of stop bits transmitted. This parameter can be a value of @ref USART_Stop_Bits */ uint16_t USART_Parity; /*!< Specifies the parity mode. This parameter can be a value of @ref USART_Parity @note When parity is enabled, the computed parity is inserted at the MSB position of the transmitted data (9th bit when the word length is set to 9 data bits; 8th bit when the word length is set to 8 data bits). */ uint16_t USART_Mode; /*!< Specifies wether the Receive or Transmit mode is enabled or disabled. This parameter can be a value of @ref USART_Mode */ uint16_t USART_HardwareFlowControl; /*!< Specifies wether the hardware flow control mode is enabled or disabled. This parameter can be a value of @ref USART_Hardware_Flow_Control */ } USART_InitTypeDef;

      USART_InitTypeDef这个结构体第一个变量名为USART_BaudRate,即为通信波特率设置,标准库函数会根据设定值计算得到USARTDIV值,从而设置USART_BRR的寄存器值,默认设置为115200。

    USART_InitTypeDef这个结构体第二个变量名为USART_WordLength,数据帧字长设置,即为通信字符中的数据位的位数设置,它设定USART_CR1寄存器M位的值。如果没有使能奇偶位校验控制,一般使用8数据位。 USART_WordLength有以下2种:8位数据位和9位数据位。

#define USART_WordLength_8b ((uint16_t)0x0000) #define USART_WordLength_9b ((uint16_t)0x1000)

    USART_InitTypeDef这个结构体第三个变量名为USART_StopBits,停止位设置,即为通信字符中的停止位的位数设置,停止位有0.5、1、1.5、2四种,默认设置为一个停止位 。

#define USART_StopBits_1 ((uint16_t)0x0000) #define USART_StopBits_0_5 ((uint16_t)0x1000) #define USART_StopBits_2 ((uint16_t)0x2000) #define USART_StopBits_1_5 ((uint16_t)0x3000)

    USART_InitTypeDef这个结构体第四个变量名为USART_Parity,奇偶校验控制选择,即为设置奇偶检验位。USART_Parity有以下三种:无奇偶检验、偶检验、奇检验,通常选择无奇偶校验。

#define USART_Parity_No ((uint16_t)0x0000) #define USART_Parity_Even ((uint16_t)0x0400) #define USART_Parity_Odd ((uint16_t)0x0600)

    USART_InitTypeDef这个结构体第五个变量名为USART_Mode,USART模式选择设置。USART模式选择有以下2个选择:USART_Mode_Rx和USART_Mode_Tx,允许使用逻辑或运算选择两个,它设定 USART_CR1寄存器的 RE 位和 TE位。所以通常设置为收发模式。

#define USART_Mode_Rx ((uint16_t)0x0004) #define USART_Mode_Tx ((uint16_t)0x0008)

    USART_InitTypeDef这个结构体第六个变量名为USART_HardwareFlowControl,硬件流控制选择设置,只有在硬件流控制模式下才有效,USART_HardwareFlowControl有以下4个选择:无,使能RTS、使能CTS、同时使能RTS和CTS。硬件流控可以控制数据传输的进程,防止数据丢失,该功能主要在收发双方传输速度不匹配的时候使用。默认选择无硬件流控。

#define USART_HardwareFlowControl_None ((uint16_t)0x0000) #define USART_HardwareFlowControl_RTS ((uint16_t)0x0100) #define USART_HardwareFlowControl_CTS ((uint16_t)0x0200) #define USART_HardwareFlowControl_RTS_CTS ((uint16_t)0x0300)

2. 初始化USART串口

 配置步骤:

 (1)打开GPIO和USART的时钟

 (2)设置USART两个管脚GPIO模式

 (3)配置USART数据格式、波特率等参数

 (4)最后使能USART功能

void USART_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; /* 第1步:打开GPIO和USART部件的时钟 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE); /* 使能USART2重映像,即IO复用 */ GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); /* 第2步:将USART Tx的GPIO配置为推挽复用模式 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOD, &GPIO_InitStructure);//PD5-USART2_TX /* 第3步:将USART Rx的GPIO配置为浮空输入模式 由于CPU复位后,GPIO缺省都是浮空输入模式,因此下面这个步骤不是必须的 但是,我还是建议加上便于阅读,并且防止其它地方修改了这个端口线的设置参数 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOD, &GPIO_InitStructure); /* 第3步已经做了,因此这步可以不做 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; */ GPIO_Init(GPIOD, &GPIO_InitStructure);//PD6-USART2_RX /* 第4步:配置USART参数 - BaudRate = 115200 baud - Word Length = 8 Bits - One Stop Bit - No parity - Hardware flow control disabled (RTS and CTS signals) - Receive and transmit enabled */ USART_InitStructure.USART_BaudRate = 115200; //波特率为115200 USART_InitStructure.USART_WordLength = USART_WordLength_8b; //设置数据位为8位 USART_InitStructure.USART_StopBits = USART_StopBits_1; //设置1位停止位 USART_InitStructure.USART_Parity = USART_Parity_No; //指定无校验位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//指定无流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//开启串口接收和发送功能 USART_Init(USART2, &USART_InitStructure);//调用库函数USART_Init初始化USART2串口 /* 第5步:使能 USART, 配置完毕 */ USART_Cmd(USART2, ENABLE); //调用库函数USART_Cmd开启串口USART2 /* CPU的小缺陷:串口配置好,如果直接Send,则第1个字节发送不出去 如下语句解决第1个字节无法正确发送出去的问题 */ USART_ClearFlag(USART2, USART_FLAG_TC); /* 清除发送完成标志,Transmission Complete flag */ }  2.1 端口复用 

      **1)定义:**外部设备简称“外设”,是指连在计算机主机以外的硬件设备,比如键盘控制芯片,液晶,A/D转换芯片等等。对数据和信息起着传输、转送和存储的作用,是计算机系统中的重要组成部分。外设可通过单片机的I/O,SPI,I2C等总线控制。单片机内部的外设一般包括:串口控制模块,SPI模块,I2C模块,A/D模块,PWM模块,CAN模块,EEPROM,比较器模块等等,它们都集成在单片机内部,有相对应的内部控制寄存器,可通过单片机指令直接控制。

       STM32有很多的内置外设,这些外设的外部引脚都是与GPIO复用的。也就是说,一个GPIO如果可以复用为内置外设的功能引脚,那么当这个GPIO作为内置外设使用的时候,就叫做复用。端口复用是将一个I/O赋予多个功能,通过设置I/O的工作模式来切换不同的功能。PD5和PD6引脚不用作GPIO,而用做复用功能USART2串口的TX,RX引脚使用的时候,就是端口复用。

   **  2)端口复用设置步骤****(PD5和PD6引脚复用):**

首先 GPIO 端口时钟使能**。**要使用到端口复用,当然要使能该端口的时钟了。RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE); **复用的外设时钟使能。**要将端口 PD5,PD6复用为USART2串口,所以要使能串口时钟。RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); 端口模式配置。GPIO_Init()函数。

**    3)引脚GPIO模式设置**

       从上面2幅图可以看出,PD5引脚复用为USART2串口发送,设置为推挽复用输出;PD6引脚复用为USART2串口接收,一般设置为浮空输入。

2.2 重映射

    1)定义:重映射是将某些I/O口上面的功能映射到其他I/O口上面去,重映射的I/O都是厂家设置好的,不能自己更改。每个内置外设都有若干个输入输出引脚,一般这些引脚的输出端口都是固定不变的,为了让设计工程师可以更好地安排引脚的走向和功能,在STM32中引入了外设引脚重映射的概念,即一个外设的引脚除了具有默认的端口外,还可以通过设置重映射寄存器的方式,把这个外设的引脚映射到其它的端口。

**    2)重映射技术的需求背景:**

I/O的复用:GPIO和内置外设共用引出管脚 I/O的重映射:复用功能(AFIO)从不同的GPIO管脚引出  方便了PCB的设计,减少了信号交叉干扰  分时复用某些外设,虚拟地增加了端口数目  为了使不同器件封装的外设IO功能数量达到最优,可以把一些复用功能重新映射到其他一些引脚上。STM32中有很多内置外设的输入输出引脚都具有重映射(remap)的功能。

**  3)AFIO重映射的操作步骤:**

使能重新映射到的I/O端口时钟

 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD , ENABLE);

使能被重新映射的外设时钟

 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

使能AFIO功能的时钟(勿忘),用于“事件控制”、“重映射”、“调试IO配置”、“外部中断”时开启AFIO时钟。

 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

进行重映射

 GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);

**   4)部分重映射 & 完全重映射**

部分重映射:功能外设的部分引脚重新映射,还有一部分引脚是原来的默认引脚。 完全重映射:功能外设的所有引脚都重新映射。

      其中REMAP[1:0]=00表示没有重映像,REMAP[1:0]=01表示部分重映像,REMAP[1:0]=11表示完全重映像。

      通过查看GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState) 第一个参数,来确定是部分映射还是完全重映射。 GPIO_partialRemap_XX是部分映射,GPIO_FullRemap_XX是完全重映射。

其中USART2串口重映射的参数只有GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);

3. USART的寄存器

      在stm32f10x.h中定义了USART的相关寄存器,如下:

typedef struct { __IO uint16_t SR; uint16_t RESERVED0; __IO uint16_t DR; uint16_t RESERVED1; __IO uint16_t BRR; uint16_t RESERVED2; __IO uint16_t CR1; uint16_t RESERVED3; __IO uint16_t CR2; uint16_t RESERVED4; __IO uint16_t CR3; uint16_t RESERVED5; __IO uint16_t GTPR; uint16_t RESERVED6; } USART_TypeDef;

     每个寄存器的描述如下:

     USART的配置就是通过写相应寄存器的相应位实现的,寄存器每一位的定义和复位值如下:

(1)串口状态寄存器USART_SR(偏移地址0x0,复位值为0xC0)

       32位的串口状态寄存器USART_SR只有第[9:0]位有效,如下图所示:

        “读DR+读SR”或“写DR+读SR”是指连续性的两个操作,即“读DR”或“写DR”后,立即进行读SR的操作。 

(2)串口数据寄存器USART_DR(偏移地址0x04)

       32位的串口数据寄存器USART_DR只有第[8:0]位有效,用于发送串口数据时记为TDR,用于接收串口数据时记为RDR,TDR和RDR是映射到同一个地址的两个物理寄存器,通过读、写指令来区分使用了哪个寄存器,即读USART_DR时自动识别为RDR,写USART_DR时自动识别为TDR。

(3)波特率寄存器USART_BRR(偏移地址0x08,复位值为0x0)

       32位的波特率寄存器USART_BRR只有第[15:0]位有效,其中,第[15:4]位记为DIV_Mantissa[11:0],第[3:0]位记为DIV_Fraction[3:0]。波特率的计算公式为:波特率 = fck / (16×USART_DIV),而USART_DIV = DIV_Mantissa + DIV_Fraction/16,对于USART2而言,fck = PCLK1=36MHz。如果波特率设为9600bps,则可配置DIV_Mantissa=234,DIV_Fraction=6;如果波特率设为115200bps,则可配置DIV_Mantissa=19,DIV_Fraction=8,实际波特率为115384bps,误差为0.15%(可接收范围内)。

(4)串口控制寄存器USART_CR1(偏移地址0x0C,复位值为0x0)

        32位的串口控制寄存器USART_CR1只有第[13:0]位有效,如下图所示。 

(5)两个控制寄存器USART_CR2和USART_CR3

       主要用于同步串行控制和流控制。其中,USART_CR2的第[13:12]位称为STOP位,为00b表示1位停止位,为01b表示0.5位停止位,为10b表示2位停止位,为11b表示1.5位停止位。默认值为00b,即1位停止位。

4. USART的基于寄存器的串口的操作

**(1)串口初始化 **

       串口初始化包括三个主要的操作,即配置串口通信的波特率、设置串口数据帧的格式以及开启串口接收中断等。

(2)发送数据

       串口发送数据一般通过函数调用实现,发送数据前应选判断前一个发送的数据是否发送完成,即判断USART_SR寄存器的TC位是否为1,如果为1表示前一个数据发送完成,则可以启动本次数据发送。发送数据只需要将待发送的数据写入串口数据寄存器USART_DR中,发送单元会按拟定的波特率将数据串行发送出去。 

**(3)接收数据 **

      串口接收数据一般通过串口接收中断实现,需要开启串口接收中断,当接收到新的数据就绪时,在串口中断服务函数中读取串口接收到的数据。 

4. USART的库函数

四、USART的编程应用

1. 编程思想

 步骤一:初始化串口。 

步骤二:检测串口接收器,如果有数据则放入串口USART_DR寄存器。 

步骤三:将接收到的字符发送给微机等设备,然后回到步骤二。

例子

  现要求从键盘输入字符,然后将输入的字符显示在串口终端上,流程图如下:



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3