当前位置:   article > 正文

[STM32]HAL库STM32CubeMX+DHT11温湿度传感器_hal库dht11

hal库dht11

目录

所用的工具:

知识概括:

DHT11介绍:

DHT11性能说明:

接口说明

 电源引脚

串行接口  

工程创建

1、设置RCC

2、时钟设置

3、项目文件设置

4、设置IO口(DATA)

5、TIM时钟配置

6、USART配置

  程序编写:

         1、TIM3实现微秒(us)级延时函数:

2、IO口配置

3、DHT11检测起始信号(这里的DHT11_LOW,DHT11_HIGH 在下面的的第8点)

4、DHT11检测响应信号(这里的DHT11_IO_IN在下面的第8点)

5、DHT11读取一bit数据

6、DHT11读取一个Byte数据

7、DHT11读取湿度和温度的数据

8、IO口输出高、低电平以及读取IO口电平定义和需要添加的头文件

9、温度、湿度和数据缓存数组的声明,函数的声明

10、主循环,(DHT11数据的读取和串口发送)

效果展示

1、将程序下载到开发板

2、串口助手查看数据​编辑

总结 

附录(完整程序)


所用的工具:

1、芯片:STM32F103C8T6

2、软件:STM32CubeMX软件

3、IDE :   MDK_Keil软件

知识概括:

DHT11基本知识和原理

DHT11通讯方式

STM32CubeTIM和USART的配置

DHT11数据采集实现

DHT11介绍:

DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传
感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高
的可靠性与卓越的长期稳定性。

DHT11性能说明:

接口说明

建议连接线短语20m时用5K的上拉电阻,大于20m时根据实际情况使用合适的上拉电阻

 电源引脚

        DHT11的供电电压为 3.3~5v。传感器上电后,要等待1s,以越过不稳定状态在此期间无需发送任何指令。电源引脚(VDD,GND)之间可增加一个100uf电容,用以去耦滤波。

串行接口  

DATA 用于微处理器与 DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分小数部分和整数部分,具体格式在下面说明,当前小数部分用于以后扩展,现读出为零.操作流程下:
           一次完整的数据传输为40bit,高位先出。
         数据格式: 8bit湿度整数数据+8bit湿度小数数据
                          +8bi温度整数数据+8bit温度小数数据
                          +8bit校验和
        数据传送正确时校验和数据等于“8bit湿度整数数据+8bit湿度小数数据 +8bi温度整数数据+8bit温度小数数据” 所得结果的末8位。
        用户MCU发送一次开始信号后,DHT11从低功耗模式转换到高速模式,等待主机开始信号结束后,DHT11发送响应信号,送出40bit的数据,并触发一次信号采集, 用户可选择读取部分数据.从模式下,DHT11接收到开始信号触发一次温湿度采集, 如果没有接收到主机发送开始信号,DHT11不会主动进行温湿度采集.采集数据后 转换到低速模式。
*接下来就是重点中的重点了!!
DHT11的通讯过程:
        

        总线空闲状态为高电平,主机把总线拉低等待DHT11响应, 主机 把总线拉低必须 大于18毫秒 ,保证DHT11能检测到起始信号。当DHT11接收到主机的起始信号之后,会拉高总线并且等待。开始信号的结束后,将DATA切换成输入模式(这里的DATA指的是那个模块上的IO口切换成输入模式),随后总线为低电平,说明 DHT11 发出 80us低电平 的响应信号,发出响应信号之后 , DHT11 再次把总线 拉高80us ,准备开始发送数据, 数据是以一bit位的方式传输 当最后一bit数据传送完毕后 DHT11 拉低总线50us,随后总线由上拉电阻拉高进入空闲态。
下面这张图的延迟时长非常重要,需要在程序中用到

         总线空闲状态为高电平, 主机 把总线拉低等待DHT11响应,主机把总线拉低必 须 大于18毫秒 ,保证DHT11能检测到起始信号。DHT11接收到主机的开始信号后, 拉高总线 20us~40us ,等待开始信号结束后, DHT11 发出 80us低电平 的响应信号,发出响应信号之后 , DHT11 再次把总线 拉高80us 并且准备输出数据。
        下面这张是DHT11数据传输的时序图:
        数字0信号表示方法如图所示:
     数字1信号表示方法如图所示:

        

         总线为低电平,说明DHT11发送响应信号,DHT11发送响应信号后,再把总线拉 高80us,准备发送数据,每一bit数据都以50us低电平时隙开始,高电平的长短定 了数据位是0还是1.格式见下面图示.如果读取响应信号为高电平,则DHT11没有 响应,请检查线路是否连接正常.当最后一bit数据传送完毕后,DHT11拉低总线 50us,随后总线由上拉电阻拉高进入空闲状态

工程创建

1、设置RCC

2、时钟设置

 3、项目文件设置

  • 1 设置项目名称
  • 2 设置存储路径
  • 3 选择所用IDE

1、 复制所用文件的.C和.H

2、每个功能生成独立的.C和.H文件

 4、设置IO口(DATA)

第二步的Moed设置GPIO_OUTPUT和GPIO_INPUT都可以,因为DHT11是单线双向的 ,所以在编写程序时我们需要改写的它IO模式。  

5、USART配置

第二步 Mode设置为异步通信 

  程序编写:

   首先来编写函数部分:

    1、微秒(us)级延时函数:

  1. /**
  2. * @brief us级延时函数
  3. * @param delay 控制延时的时长
  4. * @retval 无
  5. */
  6. void Delay_us(uint32_t udelay)
  7. {
  8. uint32_t startval,tickn,delays,wait;
  9. startval = SysTick->VAL;
  10. tickn = HAL_GetTick();
  11. delays =udelay * 72;
  12. if(delays > startval)
  13. {
  14. while(HAL_GetTick() == tickn)
  15. {
  16. }
  17. wait = 72000 + startval - delays;
  18. while(wait < SysTick->VAL)
  19. {
  20. }
  21. }
  22. else
  23. {
  24. wait = startval - delays;
  25. while(wait < SysTick->VAL && HAL_GetTick() == tickn)
  26. {
  27. }
  28. }
  29. }

该函数用了逻辑分析仪进行了比较,示图如下:

100us:

10us:

1us:

2、IO口配置

  1. /**
  2. * @brief DATA引脚(PA7)设置为输出模式
  3. * @param 无
  4. * @retval 无
  5. */
  6. void DHT11_OUT(void)
  7. {
  8. GPIO_InitTypeDef GPIO_InitStruct = {0};
  9. GPIO_InitStruct.Pin = GPIO_PIN_7;
  10. GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  11. GPIO_InitStruct.Pull = GPIO_PULLUP;
  12. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  13. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  14. }
  15. /**
  16. * @brief DATA引脚(PA7)设置为输入模式
  17. * @param 无
  18. * @retval 无
  19. */
  20. void DHT11_IN(void)
  21. {
  22. GPIO_InitTypeDef GPIO_InitStruct = {0};
  23. GPIO_InitStruct.Pin = GPIO_PIN_7;
  24. GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  25. GPIO_InitStruct.Pull = GPIO_NOPULL;
  26. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  27. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  28. }

3、DHT11检测起始信号(这里的DHT11_LOW,DHT11_HIGH 在下面的的第8点

  1. /**
  2. * @brief DHT11检测起始信号
  3. * @param 无
  4. * @retval 无
  5. */
  6. void DHT11_Strat(void)
  7. {
  8. DHT11_OUT(); //PA7设置为输出模式
  9. DHT11_LOW; //主机拉低总线
  10. HAL_Delay(20); //延迟必须大于18ms ;
  11. DHT11_HIGH; //主机拉高总线等待DHT11响应
  12. Delay_us(30);
  13. }

4、DHT11检测响应信号(这里的DHT11_IO_IN在下面的第8点

  1. /**
  2. * @brief DHT11发送响应信号
  3. * @param 无
  4. * @retval 返回值0/1 0:响应成功 1:响应失败
  5. */
  6. uint8_t DHT11_Check(void)
  7. {
  8. uint8_t retry = 0 ;
  9. DHT11_IN();
  10. //采用while循环的方式检测响应信号
  11. while(DHT11_IO_IN && retry <100) // DHT11会拉低 40us ~80us
  12. {
  13. retry++;
  14. Delay_us(1);//1us
  15. }
  16. if(retry>=100) //判断当DHT11延迟超过80us时return 1 , 说明响应失败
  17. {return 1;}
  18. else retry = 0 ;
  19. while(!DHT11_IO_IN && retry<100)// // DHT11拉低之后会拉高 40us ~80us
  20. {
  21. retry++;
  22. Delay_us(1);//1us
  23. }
  24. if(retry>=100)
  25. {return 1;}
  26. return 0 ;
  27. }

5、DHT11读取一bit数据

  1. /**
  2. * @brief DHT11读取一位数据
  3. * @param 无
  4. * @retval 返回值0/1 1:读取成功 0:读取失败
  5. */
  6. uint8_t DHT11_Read_Bit(void)
  7. {
  8. uint8_t retry = 0 ;
  9. while(DHT11_IO_IN && retry <100)//同上采用while循环的方式去采集数据
  10. {
  11. retry++;
  12. Delay_us(1);
  13. }
  14. retry = 0 ;
  15. while(!DHT11_IO_IN && retry<100)
  16. {
  17. retry++;
  18. Delay_us(1);
  19. }
  20. Delay_us(40); //结束信号,延时40us
  21. if(DHT11_IO_IN) return 1; //结束信号后,总线会被拉高 则返回1表示读取成功
  22. else
  23. return 0 ;
  24. }

6、DHT11读取一个Byte数据

  1. /**
  2. * @brief DHT11读取一个字节数据
  3. * @param 无
  4. * @retval 返回值:dat 将采集到的一个字节的数据返回
  5. */
  6. uint8_t DHT11_Read_Byte(void)
  7. {
  8. uint8_t i , dat ;
  9. dat = 0 ;
  10. for(i=0; i<8; i++)
  11. {
  12. dat <<= 1; //通过左移存储数据
  13. dat |= DHT11_Read_Bit();
  14. }
  15. return dat ;
  16. }

7、DHT11读取湿度和温度的数据

  1. /**
  2. * @brief DHT11读取数据
  3. * @param temp:温度值 humi :湿度值
  4. * @retval 返回值0/1 0:读取数据成功 1:读取数据失败
  5. */
  6. uint8_t DHT11_Read_Data(uint8_t* temp , uint8_t* humi)
  7. {
  8. uint8_t buf[5]; //储存五位数据
  9. uint8_t i;
  10. DHT11_Strat(); //起始信号
  11. if(DHT11_Check() == 0) //响应信号
  12. {
  13. for(i=0; i<5; i++)
  14. {
  15. buf[i] = DHT11_Read_Byte();
  16. }
  17. if(buf[0]+buf[1]+buf[2]+buf[3] == buf[4]) //校验数据
  18. {
  19. *humi = buf[0]; // 湿度
  20. *temp = buf[2]; // 温度
  21. }
  22. }else return 1;
  23. return 0 ;
  24. }

以上就是需要用到的函数部分的编写,接下来是一些声明和定义:

8、IO口输出高、低电平以及读取IO口电平定义和需要添加的头文件

  1. /* Private define ------------------------------------------------------------*/
  2. /* USER CODE BEGIN PD */
  3. #define DHT11_HIGH HAL_GPIO_WritePin(GPIOA, DHT11_Pin, GPIO_PIN_SET) //输出高电平
  4. #define DHT11_LOW HAL_GPIO_WritePin(GPIOA, DHT11_Pin, GPIO_PIN_RESET)//输出低电平
  5. #define DHT11_IO_IN HAL_GPIO_ReadPin(GPIOA, DHT11_Pin)//读取IO口电平
  6. /* USER CODE END PD */
  1. /* Private includes ----------------------------------------------------------*/
  2. /* USER CODE BEGIN Includes */
  3. #include "stdio.h"
  4. #include "string.h"
  5. /* USER CODE END Includes */

9、温度、湿度和数据缓存数组的声明,函数的声明

  1. /* USER CODE BEGIN 1 */
  2. uint8_t temperature = 1; //温度
  3. uint8_t humidity = 1; //湿度
  4. uint8_t aTXbuf[32]; //串口发送缓存数组
  5. DHT11_Strat(void);
  6. /* USER CODE END 1 */
  1. /* Private function prototypes -----------------------------------------------*/
  2. void SystemClock_Config(void);
  3. void Delay_us(uint16_t delay); //us级延时函数
  4. //DHT11_DATA ,IO口设置为输出模式(这里的IO口指的是STM32CubeMX中配置的IO口)
  5. void DHT11_OUT(void);
  6. void DHT11_IN(void); //DHT11_Data IO设置为输入模式
  7. void DHT11_Strat(void); //主机发出起始信号
  8. uint8_t DHT11_Check(void); //DHT11发送响应信号
  9. uint8_t DHT11_Read_Bit(void); //读取DHT11一个BIT的数据
  10. uint8_t DHT11_Read_Byte(void); //读取DHT11一个Byte的数据
  11. uint8_t DHT11_Read_Data(uint8_t* temp , uint8_t* humi); //读取DHT11湿度和温度的数据
  12. /* USER CODE BEGIN PFP */

10、主循环,(DHT11数据的读取和串口发送)

  1. while (1)
  2. {
  3. //接收DHT11的温度和湿度的值
  4. DHT11_Read_Data(&temperature , &humidity);
  5. //将数据存放到aTXbuf这个数组当中去。 不了解"sprintf"用法的可以去查一下...
  6. sprintf((char*)aTXbuf,"温度:%d℃,湿度: %d %%\r\n" ,temperature ,humidity);
  7. //将数据通过串口发送到主机上的串口助手
  8. HAL_UART_Transmit(&huart1, aTXbuf, strlen((const char*)aTXbuf), 200);
  9. //延时500ms
  10. HAL_Delay(500);
  11. /* USER CODE END WHILE */
  12. /* USER CODE BEGIN 3 */
  13. }

到这里DHT11的程序就编写完了 ,下面就来看看效果吧 

效果展示

1、将程序下载到开发板

 2、串口助手查看数据

总结 

通过本次DHT11温湿度传感器的学习,掌握了单线双向的数据通信方式DHT11和主机之间数据通讯过程,而且在此过程中要注意延时函数的把控精度,在编写程序中DHT11数据读取方式,数据传输方式和数据存储方式,以及微秒级延迟函数的编写,本设计参考正点原子探索者开发板教程STM32Cube高效开发指南(高级篇)

附录(完整程序)

  1. /* USER CODE END Header */
  2. /* Includes ------------------------------------------------------------------*/
  3. #include "main.h"
  4. #include "tim.h"
  5. #include "usart.h"
  6. #include "gpio.h"
  7. /* Private includes ----------------------------------------------------------*/
  8. /* USER CODE BEGIN Includes */
  9. #include "stdio.h"
  10. #include "string.h"
  11. /* USER CODE END Includes */
  12. /* Private typedef -----------------------------------------------------------*/
  13. /* USER CODE BEGIN PTD */
  14. /* USER CODE END PTD */
  15. /* Private define ------------------------------------------------------------*/
  16. /* USER CODE BEGIN PD */
  17. #define DHT11_HIGH HAL_GPIO_WritePin(GPIOA, DHT11_Pin, GPIO_PIN_SET)
  18. #define DHT11_LOW HAL_GPIO_WritePin(GPIOA, DHT11_Pin, GPIO_PIN_RESET)
  19. #define DHT11_IO_IN HAL_GPIO_ReadPin(GPIOA, DHT11_Pin)
  20. /* USER CODE END PD */
  21. /* Private macro -------------------------------------------------------------*/
  22. /* USER CODE BEGIN PM */
  23. /* USER CODE END PM */
  24. /* Private variables ---------------------------------------------------------*/
  25. /* USER CODE BEGIN PV */
  26. /* USER CODE END PV */
  27. /* Private function prototypes -----------------------------------------------*/
  28. void SystemClock_Config(void);
  29. void Delay_us(uint16_t delay);
  30. void DHT11_OUT(void);
  31. void DHT11_IN(void);
  32. void DHT11_Strat(void);
  33. uint8_t DHT11_Check(void);
  34. uint8_t DHT11_Read_Bit(void);
  35. uint8_t DHT11_Read_Byte(void);
  36. uint8_t DHT11_Read_Data(uint8_t* temp , uint8_t* humi);
  37. /* USER CODE BEGIN PFP */
  38. /* USER CODE END PFP */
  39. /* Private user code ---------------------------------------------------------*/
  40. /* USER CODE BEGIN 0 */
  41. /* USER CODE END 0 */
  42. /**
  43. * @brief The application entry point.
  44. * @retval int
  45. */
  46. int main(void)
  47. {
  48. /* USER CODE BEGIN 1 */
  49. uint8_t temperature = 1 ;
  50. uint8_t humidity = 1;
  51. uint8_t aTXbuf[32] ;
  52. /* USER CODE END 1 */
  53. /* MCU Configuration--------------------------------------------------------*/
  54. /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  55. HAL_Init();
  56. /* USER CODE BEGIN Init */
  57. /* USER CODE END Init */
  58. /* Configure the system clock */
  59. SystemClock_Config();
  60. /* USER CODE BEGIN SysInit */
  61. /* USER CODE END SysInit */
  62. /* Initialize all configured peripherals */
  63. MX_GPIO_Init();
  64. MX_USART1_UART_Init();
  65. MX_TIM3_Init();
  66. /* USER CODE BEGIN 2 */
  67. DHT11_Strat();
  68. /* USER CODE END 2 */
  69. /* Infinite loop */
  70. /* USER CODE BEGIN WHILE */
  71. while (1)
  72. {
  73. DHT11_Read_Data(&temperature , &humidity);
  74. sprintf((char*)aTXbuf,"温度:%d℃ , 湿度:%d %% \r\n" ,temperature ,humidity);
  75. HAL_UART_Transmit(&huart1, aTXbuf, strlen((const char*)aTXbuf), 200);
  76. HAL_Delay(500);
  77. /* USER CODE END WHILE */
  78. /* USER CODE BEGIN 3 */
  79. }
  80. /* USER CODE END 3 */
  81. }
  82. /**
  83. * @brief System Clock Configuration
  84. * @retval None
  85. */
  86. void SystemClock_Config(void)
  87. {
  88. RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  89. RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  90. /** Initializes the CPU, AHB and APB busses clocks
  91. */
  92. RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  93. RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  94. RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  95. RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  96. RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  97. RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  98. RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  99. if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  100. {
  101. Error_Handler();
  102. }
  103. /** Initializes the CPU, AHB and APB busses clocks
  104. */
  105. RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
  106. |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  107. RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  108. RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  109. RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  110. RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  111. if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  112. {
  113. Error_Handler();
  114. }
  115. }
  116. /* USER CODE BEGIN 4 */
  117. void Delay_us(uint16_t delay)
  118. {
  119. __HAL_TIM_DISABLE(&htim3);
  120. __HAL_TIM_SET_COUNTER(&htim3,0);
  121. __HAL_TIM_ENABLE(&htim3);
  122. uint16_t curCnt=0;
  123. while(1)
  124. {
  125. curCnt=__HAL_TIM_GET_COUNTER(&htim3);
  126. if(curCnt>=delay)
  127. break;
  128. }
  129. __HAL_TIM_DISABLE(&htim3);
  130. }
  131. void DHT11_OUT(void)
  132. {
  133. GPIO_InitTypeDef GPIO_InitStruct = {0};
  134. GPIO_InitStruct.Pin = GPIO_PIN_7;
  135. GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  136. GPIO_InitStruct.Pull = GPIO_PULLUP;
  137. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  138. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  139. }
  140. void DHT11_IN(void)
  141. {
  142. GPIO_InitTypeDef GPIO_InitStruct = {0};
  143. GPIO_InitStruct.Pin = GPIO_PIN_7;
  144. GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  145. GPIO_InitStruct.Pull = GPIO_NOPULL;
  146. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  147. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  148. }
  149. void DHT11_Strat(void)
  150. {
  151. DHT11_OUT();
  152. DHT11_LOW;
  153. HAL_Delay(20);
  154. DHT11_HIGH;
  155. Delay_us(30);
  156. }
  157. uint8_t DHT11_Check(void)
  158. {
  159. uint8_t retry = 0 ;
  160. DHT11_IN();
  161. while(DHT11_IO_IN && retry <100)
  162. {
  163. retry++;
  164. Delay_us(1);//1us
  165. }
  166. if(retry>=100)
  167. {return 1;}
  168. else retry = 0 ;
  169. while(!DHT11_IO_IN && retry<100)
  170. {
  171. retry++;
  172. Delay_us(1);//1us
  173. }
  174. if(retry>=100)
  175. {return 1;}
  176. return 0 ;
  177. }
  178. uint8_t DHT11_Read_Bit(void)
  179. {
  180. uint8_t retry = 0 ;
  181. while(DHT11_IO_IN && retry <100)
  182. {
  183. retry++;
  184. Delay_us(1);
  185. }
  186. retry = 0 ;
  187. while(!DHT11_IO_IN && retry<100)
  188. {
  189. retry++;
  190. Delay_us(1);
  191. }
  192. Delay_us(40);
  193. if(DHT11_IO_IN) return 1;
  194. else
  195. return 0 ;
  196. }
  197. uint8_t DHT11_Read_Byte(void)
  198. {
  199. uint8_t i , dat ;
  200. dat = 0 ;
  201. for(i=0; i<8; i++)
  202. {
  203. dat <<= 1;
  204. dat |= DHT11_Read_Bit();
  205. }
  206. return dat ;
  207. }
  208. uint8_t DHT11_Read_Data(uint8_t* temp , uint8_t* humi)
  209. {
  210. uint8_t buf[5];
  211. uint8_t i;
  212. DHT11_Strat();
  213. if(DHT11_Check() == 0)
  214. {
  215. for(i=0; i<5; i++)
  216. {
  217. buf[i] = DHT11_Read_Byte();
  218. }
  219. if(buf[0]+buf[1]+buf[2]+buf[3] == buf[4])
  220. {
  221. *humi = buf[0];
  222. *temp = buf[2];
  223. }
  224. }else return 1;
  225. return 0 ;
  226. }
  227. /* USER CODE END 4 */

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/134267
推荐阅读
相关标签
  

闽ICP备14008679号