当前位置:   article > 正文

OpenMV使用心得(一)串口通信(附完整代码)_openmv p3

openmv p3

目录

前言

一、硬件连接

1.OpenMV端

2.STM32zet6端

3.msp432p401r端

二、软件实现

1.OpenMV代码讲解及实现

2.stm32zet6代码讲解及实现

3.msp432p401r代码讲解及实现

前言

本篇文章从硬件层面到软件层面详细介绍了如何实现OpenMV与STM32/msp432p401r串口通信,并介绍了一下串口通讯协议的原理,文末附上完整的源代码。

一、硬件连

1.OpenMV端

由图知RX—P5 ---TX—P4

2.STM32zet6端

本文以RX_PA3 --TX_PA2为例

3.msp432p401r端

本文以RX_P3^2--TX_P3^3 为例

 

 注意:连接引脚时一定要注意主控的TX连接OpenMV的RX,主控的RX连接OpenMV的TX

二、软件实现

1.OpenMV代码讲解及实现

运行条件:主控先通过串口给OpenMV发送char类型数据'0'作为运行模式。针对电赛需求,OpenMV只需要收到模式数据即可,可以不加帧头与帧尾,而直接判断获取的内容,详细代码如下。

  1. # 作者 记得开心①点
  2. # 创作日期 2023.8.7
  3. import sensor, image, time ,math,pyb
  4. from pyb import UART
  5. uart = UART(3,115200)#设置串口波特率
  6. #摄像头初始化
  7. sensor.reset()#复位和初始化传感器。
  8. sensor.set_pixformat(sensor.RGB565)#设置像素格式为RGB565(或GRAYSCAL)
  9. sensor.set_framesize(sensor.VGA)#设置帧大小为VGA (480x640)
  10. sensor.skip_frames(time = 2000)#等待设置生效
  11. sensor.set_auto_gain(False)#关闭自动增益
  12. sensor.set_auto_whitebal(False) #关闭白平衡
  13. clock = time.clock()#创建一个时钟对象来跟踪FPS
  14. #初始化三个板载灯
  15. red_led = pyb.LED(1)
  16. green_led = pyb.LED(2)
  17. blue_led = pyb.LED(3)
  18. red_led.on();#OpenMV初始化成功长亮红灯
  19. #红色阈值
  20. red_threshold = (0, 100, 12, 73, -123, 127)
  21. #初始化所需标志位
  22. i=0
  23. pattern=-1#模式
  24. #寻找最大色块
  25. def find_max(blobs):
  26. max_blob = None
  27. max_size = 0
  28. for blob in blobs:
  29. if blob[2]*blob[3]>max_size:
  30. max_blob=blob
  31. max_size=blob[2]*blob[3]
  32. return max_blob
  33. #获取数据
  34. def data_get():
  35. global i
  36. global pattern
  37. getrx = uart.readchar()#获取char类型数据
  38. if getrx == 0 or getrx==1 or getrx==2 or getrx==3:
  39. pattern = getrx
  40. else:
  41. i=i+1
  42. if i>=20:#如果收到的数据不正确20次以上,OpenMV闪蓝灯
  43. for i in range(1,10):
  44. for j in range(1,30):
  45. blue_led.on()
  46. for j in range(1,30):
  47. blue_led.off()
  48. #主循环
  49. while(True):
  50. clock.tick()
  51. if uart.any():#串口有任何值发送过来
  52. data_get()#获取运行模式
  53. if pattern==0:
  54. img = sensor.snapshot().lens_corr(1.6)#鱼眼矫正
  55. blobs = img.find_blobs([red_threshold],roi=[80,80,320,320])
  56. if blobs:
  57. red_led.off()
  58. green_led.on()#找到色块亮绿灯
  59. blue_led.off()
  60. max_blob = find_max(blobs)#返回最大的色块
  61. img.draw_rectangle(max_blob.rect())#画出找到色块
  62. #发送数据
  63. data=bytearray([0xa3,0xb3,int(max_blob.cx()/2),int(max_blob.cy()/2),0xc3])
  64. uart.write(data)
  65. else:#未找到色块返回0,0n
  66. red_led.off()
  67. green_led.on()#未找到色块亮青蓝色灯
  68. blue_led.on()
  69. #发送数据
  70. data=bytearray([0xa3,0xb3,int(0),int(0),0xc3])
  71. uart.write(data)
  72. else:#摄像头未执行任何模式返回255,255
  73. red_led.on()
  74. green_led.on()#未执行任何模式亮黄灯
  75. blue_led.off()
  76. #发送数据
  77. data=bytearray([0xa3,0xb3,int(255),int(255),0xc3])
  78. uart.write(data)

2.stm32zet6代码讲解及实现

/*****openmv.h*****/(32部分源文件以连接的硬件命名)

  1. //作者 记得开心①点
  2. //日期 2023.8.7
  3. #ifndef __OPENMV_H
  4. #define __OPENMV_H
  5. #include <stdio.h>
  6. extern int openmv_data[4];//已经声明全局变量,在任意文件直接调用即可
  7. void openmv_Init(void);
  8. void openmv_SendByte(uint8_t Byte);
  9. #endif

/*****openmv.c*****/(32部分源文件以连接的硬件命名)

  1. //作者 记得开心①点
  2. //日期 2023.8.7
  3. #include "stm32f10x.h" // Device header
  4. void openmv_Init(void)
  5. {
  6. RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
  7. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  8. GPIO_InitTypeDef GPIO_InitStructure;
  9. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//推挽输出模式
  10. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
  11. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  12. GPIO_Init(GPIOA, &GPIO_InitStructure);
  13. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//ÉÏÀ­ÊäÈë»ò¸¡¿ÕÊäÈë
  14. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
  15. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  16. GPIO_Init(GPIOA, &GPIO_InitStructure);
  17. USART_InitTypeDef USART_InitStructure;
  18. USART_InitStructure.USART_BaudRate = 115200;//²¨ÌØÂÊ
  19. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制
  20. USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//串口模式配置
  21. USART_InitStructure.USART_Parity = USART_Parity_No;//无校验位
  22. USART_InitStructure.USART_StopBits = USART_StopBits_1;//一位停止位
  23. USART_InitStructure.USART_WordLength = USART_WordLength_8b;//八位(不需要校验)
  24. USART_Init(USART2, &USART_InitStructure);
  25. USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
  26. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  27. NVIC_InitTypeDef NVIC_InitStructure;
  28. NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
  29. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  30. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  31. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  32. NVIC_Init(&NVIC_InitStructure);
  33. USART_Cmd(USART2, ENABLE);
  34. }
  35. void openmv_SendByte(uint8_t Byte)
  36. {
  37. USART_SendData(USART2, Byte);
  38. while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);//等待发送完成
  39. }
  40. int openmv_flag=0;
  41. int openmv_i=0;
  42. int openmv_data[4]={0};
  43. int openmv_rx=0;
  44. void USART2_IRQHandler(void)
  45. {
  46. if (USART_GetITStatus(USART2, USART_IT_RXNE) == SET)//判断接受中断标志位
  47. {
  48. openmv_rx=USART_ReceiveData(USART2);//获取数据
  49. if(openmv_flag==0&&openmv_rx==0xa3)//判断第一个帧头
  50. {
  51. openmv_flag=1;
  52. }
  53. else if(openmv_flag==1&&openmv_rx==0xb3)判断第二个帧头
  54. {
  55. openmv_flag=2;
  56. }
  57. else if(openmv_flag==3)
  58. {
  59. openmv_flag=3;
  60. openmv_data[openmv_i++]=openmv_rx;//获取数据
  61. }
  62. else if(openmv_flag==3)
  63. {
  64. openmv_flag=4;
  65. openmv_data[openmv_i++]=openmv_rx;//获取数据
  66. }
  67. else if(openmv_flag==4&&openmv_rx==0xc3)//判断最后一个帧头
  68. {
  69. openmv_flag=0;
  70. openmv_i=0;
  71. }
  72. USART_ClearITPendingBit(USART2, USART_IT_RXNE);//清除接收中断标志位
  73. }
  74. }

3.msp432p401r代码讲解及实现

/*****openmv4.h*****/(msp432部分源文件以连接的硬件命名)

这里baudrate_calculate文件附在其后,这个文件创建后在openmv4.h中包含即可,感谢某站up,m-RNA的msp432p401r开源视频教程。

  1. ​​//作者 记得开心①点
  2. //日期 2023.8.7
  3. #ifndef _openmv4_H
  4. #define _openmv4_H
  5. #include "sysinit.h"
  6. #include "baudrate_calculate.h"
  7. extern int openmv_data[8];//已经声明全局变量,在任意文件内直接调用即可
  8. void openmv_sentdata(char mode);
  9. void openmv4_uart_init(void);
  10. #endif

/*****openmv4.c*****/(msp432部分源文件以连接的硬件命名)

  1. //作者 记得开心①点
  2. //日期 2023.8.7
  3. #include "openmv4.h"
  4. //初始化IO 串口2
  5. // bound:波特率
  6. void openmv4_uart_init(uint32_t baudRate)
  7. {
  8. //固件库v3_40_01_02
  9. //默认SMCLK 48MHz 比特率
  10. const eUSCI_UART_ConfigV1 uartConfig =
  11. {
  12. EUSCI_A_UART_CLOCKSOURCE_SMCLK, //48mhz时钟打开
  13. 312, // BRDIV = 312
  14. 8, // UCxBRF = 8
  15. 0, // UCxBRS = 1
  16. EUSCI_A_UART_NO_PARITY, // 无校验位
  17. EUSCI_A_UART_LSB_FIRST, // MSB First
  18. EUSCI_A_UART_ONE_STOP_BIT, //一位停止位
  19. EUSCI_A_UART_MODE, // 串口模式配置
  20. EUSCI_A_UART_OVERSAMPLING_BAUDRATE_GENERATION, // Oversampling
  21. EUSCI_A_UART_8_BIT_LEN // 字长八位(无需校验位)
  22. };
  23. eusci_calcBaudDividers((eUSCI_UART_ConfigV1 *)&uartConfig, baudRate); //配置波特率
  24. // 1.配置GPIO复用
  25. MAP_GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_P3, GPIO_PIN2 | GPIO_PIN3, GPIO_PRIMARY_MODULE_FUNCTION);
  26. MAP_UART_initModule(EUSCI_A2_BASE, &uartConfig); // 3.初始化串口
  27. MAP_UART_enableModule(EUSCI_A2_BASE); // 4.开启串口模块
  28. MAP_UART_enableInterrupt(EUSCI_A2_BASE, EUSCI_A_UART_RECEIVE_INTERRUPT); // 5.开启接收中断
  29. //MAP_UART_enableInterrupt(EUSCI_A2_BASE, EUSCI_A_UART_TRANSMIT_INTERRUPT);//6.开启发送中断
  30. MAP_Interrupt_enableInterrupt(INT_EUSCIA2); // 7.开启串口端口中断
  31. }
  32. void openmv_sentdata(char mode)
  33. {
  34. //MAP_UART_transmitData(EUSCI_A2_BASE,0xa3);
  35. MAP_UART_transmitData(EUSCI_A2_BASE,mode);
  36. }
  37. int openmv_flag=0;
  38. int rx_data=0;
  39. int openmv_data[8]={0};
  40. int openmv_i=0;
  41. // Uart2接收中断
  42. void EUSCIA2_IRQHandler(void)
  43. {
  44. uint32_t status = MAP_UART_getEnabledInterruptStatus(EUSCI_A2_BASE);
  45. if (status & EUSCI_A_UART_RECEIVE_INTERRUPT_FLAG) //判断接受中断标志位
  46. {
  47. rx_data=MAP_UART_receiveData(EUSCI_A2_BASE);
  48. if(rx_data==0xA3&&openmv_flag==0)//判断头帧
  49. {
  50. openmv_flag=1;
  51. }
  52. else if(openmv_flag==1&&rx_data==0xb3)//判断第二帧
  53. {
  54. openmv_flag=2;
  55. }
  56. else if(openmv_flag==2)
  57. {
  58. openmv_flag=3;
  59. openmv_data[openmv_i++]=rx_data;//获取回传
  60. }
  61. else if(openmv_flag==3)
  62. {
  63. openmv_flag=4;
  64. openmv_data[openmv_i++]=rx_data;//获取回传
  65. }
  66. else if(openmv_flag==4&&rx_data==0xc3)//判断第三帧
  67. {
  68. openmv_flag=0;
  69. openmv_i=0;
  70. }
  71. MAP_UART_clearInterruptFlag(EUSCI_A2_BASE, EUSCI_A_UART_RECEIVE_INTERRUPT_FLAG); // 清除接收中断标志位
  72. }
  73. }
  74. void eusci_calcBaudDividers(eUSCI_UART_ConfigV1 *uart_config, uint32_t baudRate) //固件库v3_40_01_02
  75. {
  76. float maxAbsErrorInByte;
  77. float minAbsError;
  78. float error;
  79. uint8_t ii;
  80. uint16_t jj;
  81. uint16_t NN;
  82. uint32_t count;
  83. uint32_t clockRate;
  84. if (!uart_config || !baudRate) //传参错误 退出函数
  85. {
  86. //uart_warning_led(); //闪烁错误指示灯10次
  87. return;
  88. }
  89. if (uart_config->selectClockSource == EUSCI_A_UART_CLOCKSOURCE_SMCLK)
  90. clockRate = MAP_CS_getSMCLK();
  91. else if (uart_config->selectClockSource == EUSCI_A_UART_CLOCKSOURCE_ACLK)
  92. clockRate = MAP_CS_getACLK();
  93. else
  94. {
  95. uart_config->selectClockSource = EUSCI_A_UART_CLOCKSOURCE_SMCLK;
  96. clockRate = MAP_CS_getSMCLK();
  97. }
  98. if (baudRate > clockRate) //判断波特率是否大于时钟频率 是则退出函数
  99. {
  100. //uart_warning_led(); //闪烁错误指示灯10次
  101. return;
  102. }
  103. //var result = {UCOS16 : 0, UCBRx : 0, UCFx : 0, UCSx : 0, maxAbsError : 0};
  104. NN = (uint16_t)((float)clockRate / (float)baudRate); //应该是不需要floor
  105. minAbsError = 100000;
  106. for (jj = 0; jj <= 255; jj++)
  107. {
  108. maxAbsErrorInByte = 0;
  109. count = 0;
  110. for (ii = 0; ii <= 10; ii++)
  111. {
  112. count += NN + bitPosition(jj, 7 - (ii % 8));
  113. //error = (ii + 1) * baudPeriod - count * clockPeriod;
  114. error = (ii + 1) / (float)baudRate - count / (float)clockRate; //为了减少变量,改为此代码
  115. if (error < 0)
  116. error = -error;
  117. if (error > maxAbsErrorInByte)
  118. maxAbsErrorInByte = error;
  119. }
  120. if (maxAbsErrorInByte - minAbsError < -7.3e-12f) //这里就是“已知问题”
  121. {
  122. minAbsError = maxAbsErrorInByte;
  123. uart_config->secondModReg = jj;
  124. }
  125. }
  126. if (NN < 20)
  127. {
  128. uart_config->overSampling = 0;
  129. uart_config->clockPrescalar = NN;
  130. uart_config->firstModReg = 0;
  131. }
  132. else
  133. {
  134. uart_config->overSampling = 1;
  135. uart_config->clockPrescalar = (uint16_t)((float)NN / 16.0f); //应该是不需要floor
  136. uart_config->firstModReg = NN - (uart_config->clockPrescalar * 16);
  137. }
  138. //return minAbsError * baudRate * 100;
  139. }

 /*****baudrate_calculate.h*****/

  1. /****************************************************/
  2. // MSP432P401R
  3. // 串口波特率计算
  4. // Bilibili:m-RNA
  5. // E-mail:m-RNA@qq.com
  6. /****************************************************/
  7. /****************************** 说明 ******************************
  8. *
  9. * 源码为TI官方编写,本人只是将JS程序移植到了C语言平台,仅作为学习使用。源码出处为:
  10. * http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSP430BaudRateConverter/index.html
  11. *
  12. * ? 已知问题:
  13. * 调试时发现某些情况下,C语言的小数的大小与JS的相差较大,
  14. * 导致了算出的UCSx(即secondModReg)不一样,
  15. * 这时如果出现不能准确传输时,请换一个波特率。
  16. *
  17. * ? 需要注意:
  18. * 波特率不能大于时钟频率,否则会退出函数
  19. *
  20. * ***************************** 版本说明 ******************************
  21. *
  22. * ? v1.2 2021/8/29
  23. * 注释掉了闪烁灯的代码
  24. *
  25. * ? v1.1 2021/8/27
  26. * 添加支持固件库v3_21_00_05
  27. *
  28. * ? v1.0 2021/8/25
  29. * 仅支持固件库v3_40_01_02
  30. *
  31. * ******************************* 结束 *******************************/
  32. #ifndef __RNA_BAUDRATE_CALCULATE_H
  33. #define __RNA_BAUDRATE_CALCULATE_H
  34. #include <ti/devices/msp432p4xx/driverlib/driverlib.h>
  35. //错误指示灯宏定义 方便移植使用
  36. //MSP432P401R 有两个红灯P1.0 P2.0
  37. //#define WARN_LED_1_PORT GPIO_PORT_P1
  38. //#define WARN_LED_2_PORT GPIO_PORT_P2
  39. //#define WARN_LED_1_PIN GPIO_PIN0
  40. //#define WARN_LED_2_PIN GPIO_PIN0
  41. //#define WARN_LED_INIT MAP_GPIO_setAsOutputPin
  42. //#define WARN_LED_ON MAP_GPIO_setOutputHighOnPin
  43. //#define WARN_LED_OFF MAP_GPIO_setOutputLowOnPin
  44. #ifdef EUSCI_A_UART_7_BIT_LEN
  45. void eusci_calcBaudDividers(eUSCI_UART_ConfigV1 *uart_config, uint32_t baudRate); //固件库v3_40_01_02
  46. #else
  47. void eusci_calcBaudDividers(eUSCI_UART_Config *uart_config, uint32_t baudRate); //固件库v3_21_00_05
  48. #endif
  49. #endif

 /*****baudrate_calculate.c*****/

  1. /****************************************************/
  2. // MSP432P401R
  3. // 串口波特率计算
  4. // Bilibili:m-RNA
  5. // E-mail:m-RNA@qq.com
  6. /****************************************************/
  7. /****************************** 说明 ******************************
  8. *
  9. * 源码为TI官方编写,本人只是将JS程序移植到了C语言平台,仅作为学习使用。源码出处为:
  10. * http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSP430BaudRateConverter/index.html
  11. *
  12. * ? 已知问题:
  13. * 调试时发现某些情况下,C语言的小数的大小与JS的相差较大,
  14. * 导致了算出的UCSx(即secondModReg)不一样,
  15. * 这时如果出现不能准确传输时,请换一个波特率。
  16. *
  17. * ? 需要注意:
  18. * 波特率不能大于时钟频率,否则会退出函数
  19. *
  20. * ***************************** 版本说明 ******************************
  21. *
  22. * ? v1.2 2021/8/29
  23. * 注释掉了闪烁灯的代码
  24. *
  25. * ? v1.1 2021/8/27
  26. * 添加支持固件库v3_21_00_05
  27. *
  28. * ? v1.0 2021/8/25
  29. * 仅支持固件库v3_40_01_02
  30. *
  31. * ******************************* 结束 *******************************/
  32. #include "baudrate_calculate.h"
  33. //void uart_warning_led(void);
  34. /*
  35. * ======== bitPosition ========
  36. * return 1(0) if the specified bit position in value is set(clear)
  37. */
  38. bool bitPosition(uint16_t value, uint16_t position)
  39. {
  40. if ((value & (1 << position)))
  41. return 1;
  42. return 0;
  43. }
  44. /*
  45. * ======== eusci_calcBaudDividers ========
  46. * computes the eUSCI_UART register settings for a given clock and baud rate
  47. *
  48. * UCOS16: the oversampling bit (0 or 1)
  49. * UCBRx: the Baud Rate Control Word
  50. * UCFx: the First modulation stage select (UCBRFx)
  51. * UCSx: the Second modulation stage select (UCBRSx)
  52. * maxAbsError: the maximum TX error for the register setting above
  53. *
  54. * The first four field names match the names used in Table 18-5,
  55. * "Recommended Settings for Typical Crystals and Baudrates", of the
  56. * MSP430FR57xx Family User's Guide (SLAU272A).
  57. */
  58. void eusci_calcBaudDividers(eUSCI_UART_ConfigV1 *uart_config, uint32_t baudRate) //固件库v3_40_01_02
  59. {
  60. float maxAbsErrorInByte;
  61. float minAbsError;
  62. float error;
  63. uint8_t ii;
  64. uint16_t jj;
  65. uint16_t NN;
  66. uint32_t count;
  67. uint32_t clockRate;
  68. if (!uart_config || !baudRate) //传参错误 退出函数
  69. {
  70. //uart_warning_led(); //闪烁错误指示灯10次
  71. return;
  72. }
  73. if (uart_config->selectClockSource == EUSCI_A_UART_CLOCKSOURCE_SMCLK)
  74. clockRate = MAP_CS_getSMCLK();
  75. else if (uart_config->selectClockSource == EUSCI_A_UART_CLOCKSOURCE_ACLK)
  76. clockRate = MAP_CS_getACLK();
  77. else
  78. {
  79. uart_config->selectClockSource = EUSCI_A_UART_CLOCKSOURCE_SMCLK;
  80. clockRate = MAP_CS_getSMCLK();
  81. }
  82. if (baudRate > clockRate) //判断波特率是否大于时钟频率 是则退出函数
  83. {
  84. //uart_warning_led(); //闪烁错误指示灯10次
  85. return;
  86. }
  87. //var result = {UCOS16 : 0, UCBRx : 0, UCFx : 0, UCSx : 0, maxAbsError : 0};
  88. NN = (uint16_t)((float)clockRate / (float)baudRate); //应该是不需要floor
  89. minAbsError = 100000;
  90. for (jj = 0; jj <= 255; jj++)
  91. {
  92. maxAbsErrorInByte = 0;
  93. count = 0;
  94. for (ii = 0; ii <= 10; ii++)
  95. {
  96. count += NN + bitPosition(jj, 7 - (ii % 8));
  97. //error = (ii + 1) * baudPeriod - count * clockPeriod;
  98. error = (ii + 1) / (float)baudRate - count / (float)clockRate; //为了减少变量,改为此代码
  99. if (error < 0)
  100. error = -error;
  101. if (error > maxAbsErrorInByte)
  102. maxAbsErrorInByte = error;
  103. }
  104. if (maxAbsErrorInByte - minAbsError < -7.3e-12f) //这里就是“已知问题”
  105. {
  106. minAbsError = maxAbsErrorInByte;
  107. uart_config->secondModReg = jj;
  108. }
  109. }
  110. if (NN < 20)
  111. {
  112. uart_config->overSampling = 0;
  113. uart_config->clockPrescalar = NN;
  114. uart_config->firstModReg = 0;
  115. }
  116. else
  117. {
  118. uart_config->overSampling = 1;
  119. uart_config->clockPrescalar = (uint16_t)((float)NN / 16.0f); //应该是不需要floor
  120. uart_config->firstModReg = NN - (uart_config->clockPrescalar * 16);
  121. }
  122. //return minAbsError * baudRate * 100;
  123. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/474096
推荐阅读
相关标签
  

闽ICP备14008679号