极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 13280|回复: 4

[新人]数码管时钟

[复制链接]
发表于 2015-7-14 17:13:33 | 显示全部楼层 |阅读模式
本帖最后由 a1039752256 于 2015-7-14 17:11 编辑

潜水了那么久,看了那么多人的教程,但由于没什么动力,迟迟都做不出什么作品。两个月前准备做一个数码管时钟,搞到现在终于做好,现在特意和坛友分享一下,一同学习

简单介绍:Arduino Nano + DS1307 + 74HC595 + 四位八段数码管 + dht11 + 光敏 + 蜂鸣器

这个时钟的功能有:时间显示,温湿度显示,整点响铃,亮度调节和串口调时


来几张靓照:




(白平衡没调好,所以看上去很惨白,但实际是红色)

设计是可拆解式的,方便日后损坏维修,更换零件:


下面详解一下:

上面是Arduino的io分配,接线还是相对简单的,只要给相应的元件接上限流电阻就行了

时钟芯片采用DS1307模块,读写数据挺简单,坛里有不少好教程,但我却在I2C地址上琢磨了好几天,最后硬试几遍才试了出来。(原来I2C的地址是7位的!一定要牢记这一点,不能再犯前车之鉴!)
读写DS1307的程序是根据pdf写的,用的是Arduino自带的I2C库。之所以没有用rtc库,是想到用库会让程序的体积白白变大不少,另外就是觉得一味用库学不到什么真东西。
下面是自己写的程序,请大神指教;)
  1. int BcdToDec(int bcd)
  2. {
  3.   return (bcd >> 4) * 10 + (bcd & 0x0F);
  4. }

  5. int DecToBcd(int dec)
  6. {
  7.   return (dec / 10) << 4 | (dec % 10);
  8. }

  9. void GetTime(int output[8])
  10. {
  11.   int bcd[8];
  12.   Wire.beginTransmission(ADDR);
  13.   Wire.write(0x00);
  14.   Wire.endTransmission();
  15.   Wire.requestFrom(ADDR, 8);
  16.   for (int i = 0; i < 7; i++)
  17.   {
  18.     bcd[i] = Wire.read();
  19.   }
  20.   output[0] = BcdToDec(bcd[0] & 0x7F);
  21.   output[1] = BcdToDec(bcd[1] & 0x7F);
  22.   output[2] = BcdToDec(bcd[2] & 0x3F);
  23.   output[3] = BcdToDec(bcd[3] & 0x07);
  24.   output[4] = BcdToDec(bcd[4] & 0x3F);
  25.   output[5] = BcdToDec(bcd[5] & 0x1F);
  26.   output[6] = BcdToDec(bcd[6]) + 2000;
  27.   output[7] = BcdToDec(bcd[7]);
  28. }

  29. void SetTime(int mode, int value)
  30. {
  31.   if (mode == 0 || mode == 1) value = constrain(value,0,59);
  32.   if (mode == 2) value = constrain(value,0,23);
  33.   if (mode == 3) value = constrain(value,1,7);
  34.   if (mode == 4) value = constrain(value,1,31);
  35.   if (mode == 5) value = constrain(value,1,12);
  36.   if (mode == 6) value = constrain(value,2000,2099)-2000;
  37.   int temp = DecToBcd(value);
  38.   Wire.beginTransmission(ADDR);
  39.   Wire.write(mode);
  40.   Wire.write(temp);
  41.   Wire.endTransmission();
  42. }
复制代码



时钟的中断来自DS1307输出的1Hz方波。本来打算在中断程序中读时间,但I2C好像跟中断程序冲突,所以用了中断标志位的方法。
  1. void update()//中断函数
  2. {
  3.   interrupt = true;
  4. }
复制代码


另外还有一点要注意,就是DS1307一旦断电(拆电池),秒寄存器的最高位(CH)会置1,芯片停振,也没有方波输出,所以我加上了CH清零的函数
  1. Wire.beginTransmission(ADDR);
  2.   Wire.write(0x00);
  3.   Wire.endTransmission();
  4.   Wire.requestFrom(ADDR, 1);
  5.   time[0] = Wire.read();
  6.   Wire.beginTransmission(ADDR);
  7.   Wire.write(0x00);
  8.   Wire.write(time[0]&B01111111);//将DS1307的秒最高位清零,否则时钟不起振
  9.   Wire.endTransmission();
复制代码



数码管是动态扫描,本来打算直接用io驱动,但是想想好像有点浪费io,所以加了74HC595,节省下了5个io,可以用来日后功能升级。
贴一下显示函数:
  1. void Display(int value, int pin, int duration)//显示函数
  2. {
  3.   shiftOut(datapin, clockpin, LSBFIRST, value);
  4.   digitalWrite(latchpin, HIGH);
  5.   digitalWrite(latchpin, LOW);
  6.   digitalWrite(pin, HIGH);
  7.   delayMicroseconds(duration);
  8.   digitalWrite(pin, LOW);
  9. }
复制代码

显示函数很简单,一目了然;)

数码管扫描周期大概是10ms,肉眼基本看不到闪烁感。数码管的亮度可以由持续时间(duration)来控制,持续时间由光敏电阻来调节,以达到简单的根据环境调光功能
  1. brightness = analogRead(A6);
  2.     duration = 1024 - brightness;//简单将亮度转换为数码管持续点亮时间
  3.     if (duration > 700) duration = 700;//过亮瞎眼
复制代码


至于dht11用的是别人的程序,这里没什么好说,就贴程序给大家参考一下吧
  1. void GetTemp()//读dht11
  2. {
  3.   digitalWrite(dht11_pin, HIGH);
  4.   delayMicroseconds(30);
  5.   pinMode(dht11_pin, INPUT);
  6.   digitalWrite(dht11_pin, INPUT_PULLUP);

  7.   dht11_timemark = micros();
  8.   while (digitalRead(dht11_pin) && micros() - dht11_timemark <= 50);//超时退出死循环
  9.   dht11_timemark = micros();
  10.   while (!digitalRead(dht11_pin) && micros() - dht11_timemark <= 90);
  11.   dht11_timemark = micros();
  12.   while (digitalRead(dht11_pin) && micros() - dht11_timemark <= 90);

  13.   //Not appreciated,but stable
  14.   for (int j = 0; j < 5; j++)
  15.   {
  16.     int data = 0;
  17.     for (int i = 0; i < 8; i++)
  18.     {
  19.       dht11_timemark = micros();
  20.       while (!digitalRead(dht11_pin) && micros() - dht11_timemark <= 60);
  21.       delayMicroseconds(30);
  22.       int state = digitalRead(dht11_pin);
  23.       data <<= 1;
  24.       data |= state;
  25.       dht11_timemark = micros();
  26.       while (digitalRead(dht11_pin) && micros() - dht11_timemark <= 50);
  27.     }
  28.     dht11_buf[j] = data;
  29.   }

  30.   //Update data
  31.   int temp = dht11_buf[0] + dht11_buf[1] + dht11_buf[2] + dht11_buf[3];
  32.   if (dht11_buf[4] == temp & 0xFF)//校验数据
  33.   {
  34.     temperature = dht11_buf[2];
  35.     humidity = dht11_buf[0];
  36.     Serial.print("Temperature : ");
  37.     Serial.print(temperature);//更新温度
  38.     Serial.print("\t");
  39.     Serial.print("Humidity : ");
  40.     Serial.println(humidity);
  41.   }
  42. }
复制代码

蜂鸣器也没什么好说,用了tone()函数

因为从一开始没打算加按键,所以调时只能通过串口调时
直接贴程序吧(有点长),看注释应该能懂
  1. int Command()//随便输入什么来进入调时程序
  2. {
  3.   int temp = 0;
  4.   delay(5);
  5.   while (Serial.available()) Serial.read();//清空串口
  6.   Serial.println();

  7. help:
  8.   Serial.print("--------------------");//分割线
  9.   Serial.print("--------------------");
  10.   Serial.print("--------------------");
  11.   Serial.println();
  12.   Serial.println("Input the following command:");
  13.   Serial.println("  0-----For help");
  14.   Serial.println("  1-----Set date");
  15.   Serial.println("  2-----Set time");
  16.   Serial.println("  9-----Exit");
  17.   Serial.println();

  18.   temp = GetChar();//读取串口指令
  19.   if (temp == -1) return TIMEOUT;//超时退出函数
  20.   else temp -= '0';//将char型的数字转换为int型

  21.   switch (temp)
  22.   {
  23.     default :
  24.       {
  25.         Serial.println("Invalid. Try again.");
  26.         Serial.println();
  27.         goto help;
  28.       }

  29.     case 0: goto help;

  30.     case 1:
  31.       {
  32. setdate:
  33.         Serial.print("--------------------");
  34.         Serial.print("--------------------");
  35.         Serial.print("--------------------");
  36.         Serial.println();
  37.         Serial.println("Date?(YYYY/MM/DD)");

  38.         if (Getstring(CMD, cmdlength) == -1) return TIMEOUT;//超时退出函数
  39.         Serial.println(CMD);
  40.         time[6] = atoi(CMD);//atoi()函数将字符串转换成数字
  41.         Strshift(CMD, cmdlength);//移位字符串(不知道有没有现成的库函数,所以自己写了一个)
  42.         time[5] = atoi(CMD);
  43.         Strshift(CMD, cmdlength);
  44.         time[4] = atoi(CMD);

  45.         if (time[6] < 2000 || time[6] > 2099 || time[5] < 1 || time[5] > 12 || time[4] < 1 || time[4] > 31)//检验数据
  46.         {
  47.           Serial.println("Invalid. Try again? (Y/N)");
  48.           Serial.println();
  49.           temp = GetChar();
  50.           if (temp == 'Y' || temp == 'y') {
  51.             Serial.println('Y');
  52.             goto setdate;
  53.           }
  54.           else return 0;//放弃修改时间返回0
  55.         }
  56.         Serial.println("OK");
  57.         Serial.println();

  58.         Serial.println("Day of week?(1 for Monday, 7 for Sunday)");//1是星期一,7是星期天
  59.         temp = GetChar();
  60.         if (temp == -1) return TIMEOUT;
  61.         time[3] = temp - '0';
  62.         Serial.println(time[3]);

  63.         if (time[3] < 1 || time[3] > 7)
  64.         {
  65.           Serial.println("Invalid. Try again? (Y/N)");
  66.           Serial.println();
  67.           int temp = GetChar();
  68.           if (temp == 'Y' || temp == 'y') {
  69.             Serial.println('Y');
  70.             goto setdate;
  71.           }
  72.           else return 0;
  73.         }
  74.         Serial.println(day[time[3]]);
  75.         Serial.println("OK");
  76.         Serial.println();

  77.         SetTime(3, time[3]);//往DS1307写入数据
  78.         SetTime(4, time[4]);
  79.         SetTime(5, time[5]);
  80.         SetTime(6, time[6]);

  81.         Serial.println("Exit? (Y/N)");
  82.         temp = GetChar();
  83.         if (temp == -1) return TIMEOUT;
  84.         if (temp == 'N' || temp == 'n')  {
  85.           Serial.println('N');
  86.           goto help;
  87.         }
  88.         else  return 1;//成功修改时间返回1
  89.       }

  90.     case 2:
  91.       {
  92. settime:
  93.         Serial.print("--------------------");
  94.         Serial.print("--------------------");
  95.         Serial.print("--------------------");
  96.         Serial.println();
  97.         Serial.println("Time?(HH:MM:SS)");

  98.         if (Getstring(CMD, cmdlength) == -1) return TIMEOUT;
  99.         Serial.println(CMD);
  100.         time[2] = atoi(CMD);
  101.         Strshift(CMD, cmdlength);
  102.         time[1] = atoi(CMD);
  103.         Strshift(CMD, cmdlength);
  104.         time[0] = atoi(CMD);

  105.         if (time[2] < 0 || time[2] > 23 || time[1] < 0 || time[1] > 59 || time[0] < 0 || time[0] > 59)
  106.         {
  107.           Serial.println("Invalid. Try again? (Y/N)");
  108.           Serial.println();
  109.           temp = GetChar();
  110.           if (temp == 'Y' || temp == 'y') {
  111.             Serial.println('Y');
  112.             goto settime;
  113.           }
  114.           else  return 0;
  115.         }
  116.         Serial.println("OK");
  117.         Serial.println();

  118.         SetTime(0, time[0]);
  119.         SetTime(1, time[1]);
  120.         SetTime(2, time[2]);

  121.         Serial.println("Exit? (Y/N)");
  122.         temp = GetChar();
  123.         if (temp == -1) return TIMEOUT;
  124.         if (temp == 'N' || temp == 'n') {
  125.           Serial.println('N');
  126.           goto help;
  127.         }
  128.         else return 1;
  129.       }

  130.     case 9: return 0;
  131.   }
  132. }
复制代码

  1. void Strshift(char str[], int length)//作用是清掉前面的数字和非数字
  2. {
  3.   while (1)
  4.   {
  5.     char temp = str[0];
  6.     for (int i = 0; i < length - 1; i++)
  7.     {
  8.       str[i] = str[i + 1];
  9.     }
  10.     if (temp < 48 || temp > 57) break;
  11.   }
  12. }

  13. int Getstring(char str[], int length)//从串口获得字符串
  14. {
  15.   for (int j = 0; j < length; j++)//清空字符串
  16.   {
  17.     str[j] = 0;
  18.   }
  19.   systime = millis();
  20.   while (!Serial.available())
  21.   {
  22.     if (millis() - systime > 10000)
  23.     {
  24.       Serial.println("Timeout");
  25.       Serial.println();
  26.       return TIMEOUT;//超时退出函数
  27.     }
  28.   }
  29.   int i = 0;
  30.   while (Serial.available())//写入字符串
  31.   {
  32.     str[i] = Serial.read();
  33.     i++;
  34.     if (i >= length) break;
  35.     delay(2);
  36.   }
  37. }

  38. int GetChar()//作用是取第一字节数据,然后清空串口
  39. {
  40.   systime = millis();
  41.   while (!Serial.available())
  42.   {
  43.     if (millis() - systime > 5000)
  44.     {
  45.       Serial.println("Timeout");
  46.       Serial.println();
  47.       while (Serial.available()) Serial.read();
  48.       return TIMEOUT;
  49.     }
  50.   }
  51.   delay(5);
  52.   int temp = Serial.read();
  53.   while (Serial.available()) Serial.read();
  54.   return temp;
  55. }
复制代码


最后说一下这个数码管时钟的特色吧,不知道你们有没有对第一幅图片感到奇怪?“0 9.1.7.”?

其实那是显示时间的界面;)
因为用了4位的数码管,不能一同显示时分秒,所以用了这样的方法来表示。数字0917代表的是时间9点17分,而下面的小数点则是表示二进制的0111,就是7,映射到时钟表盘上就是7到8的区间,代表的是现在是在35秒到39秒之间!再举一例吧,“1 6.5.2”代表的就是16点52分,30至35秒!这样做弥补了4位数码管不能同时显示时分秒的缺陷,同时充分运用了数码管的小数点资源,还略带透出一点极客范~  看了那么多人的作品,好像没看到有人这么做过,不知道我能不能称上第一人呢?→_→
哈哈,程序的具体看下面
  1. int temp = time[0] / 5; //将秒映射到0~11
  2.     dvalue[3] = num[time[2] / 10] | (temp >> 3 & 0x01);//更新显存(后半部分用来显示 秒/5 的二进制)
  3.     dvalue[2] = num[time[2] % 10] | (temp >> 2 & 0x01);
  4.     dvalue[1] = num[time[1] / 10] | (temp >> 1 & 0x01);
  5.     dvalue[0] = num[time[1] % 10] | (temp >> 0 & 0x01);
复制代码


最后的最后,贴出主程序
  1. /*  Clock  */
  2. /*  DS1307 + DHT11 + 7-digital segment + AT24C32 + Arduino Nano  */
  3. #include <Wire.h>
  4. #define TIMEOUT -1//超时
  5. #define ADDR 0x68//DS1307 I2C地址

  6. char CMD[16];//储存串口指令
  7. int cmdlength = sizeof(CMD) / sizeof(CMD[0]);

  8. bool interrupt = false;//中断标志
  9. unsigned long systime;//system time
  10. char day[8][4] = {"---", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};//星期

  11. int time[8];//储存时间信息

  12. int dht11_pin = 11;
  13. int dht11_mark = 1;
  14. unsigned long dht11_timemark = 0;
  15. int dht11_buf[5];//暂存温湿度数据
  16. int temperature;
  17. int humidity;

  18. int buzzer_pin = 12;
  19. int buzzer_mark = 1 ;
  20. unsigned long buzzer_timemark = 0;

  21. int brightness;//亮度

  22. int duration = 500;//点亮一位数码管的时间
  23. int dvalue[4];//数码管显存
  24. int num[10] = {B11111100, B01100000, B11011010, B11110010, B01100110,
  25.                B10110110, B10111110, B11100000, B11111110, B11110110
  26.               };//abcdefgdp
  27. int datapin = 4;//74HC595
  28. int clockpin = 6;
  29. int latchpin = 5;
  30. int dpin[4] = {17, 16, 15, 14}; //数码管d0,d1,d2,d3


  31. void update()//中断函数
  32. {
  33.   interrupt = true;
  34. }

  35. void Display(int value, int pin, int duration)//显示函数
  36. {
  37.   shiftOut(datapin, clockpin, LSBFIRST, value);
  38.   digitalWrite(latchpin, HIGH);
  39.   digitalWrite(latchpin, LOW);
  40.   digitalWrite(pin, HIGH);
  41.   delayMicroseconds(duration);
  42.   digitalWrite(pin, LOW);
  43. }

  44. void setup() {
  45.   Wire.begin();
  46.   Serial.begin(9600);
  47.   pinMode(buzzer_pin, OUTPUT);
  48.   pinMode(datapin, OUTPUT);
  49.   pinMode(clockpin, OUTPUT);
  50.   pinMode(latchpin, OUTPUT);
  51.   for (int i = 0; i < 4; i++)
  52.   {
  53.     pinMode(dpin[i], OUTPUT);
  54.   }
  55.   pinMode(13, OUTPUT);

  56.   pinMode(dht11_pin, OUTPUT);
  57.   digitalWrite(dht11_pin, LOW);
  58.   delay(18);
  59.   GetTemp();//获取温度

  60.   Wire.beginTransmission(ADDR);
  61.   Wire.write(0x00);
  62.   Wire.endTransmission();
  63.   Wire.requestFrom(ADDR, 1);
  64.   time[0] = Wire.read();
  65.   Wire.beginTransmission(ADDR);
  66.   Wire.write(0x00);
  67.   Wire.write(time[0]&B01111111);//将DS1307的秒最高位清零,否则时钟不起振
  68.   Wire.endTransmission();

  69.   SetTime(7, 10);//使能方波

  70.   attachInterrupt(0, update, FALLING);//下降沿进入中断

  71. }

  72. void loop() {
  73.   if (interrupt)
  74.   {
  75.     interrupt = false;
  76.     GetTime(time);
  77.     int temp = time[0] / 5; //将秒映射到0~11
  78.     dvalue[3] = num[time[2] / 10] | (temp >> 3 & 0x01);//更新显存(后半部分用来显示 秒/5 的二进制)
  79.     dvalue[2] = num[time[2] % 10] | (temp >> 2 & 0x01);
  80.     dvalue[1] = num[time[1] / 10] | (temp >> 1 & 0x01);
  81.     dvalue[0] = num[time[1] % 10] | (temp >> 0 & 0x01);
  82.     digitalWrite(13, time[0] & 0x01); //秒为奇数的时候点亮
  83.     Serial.print(time[6]);//打印时间
  84.     Serial.print("/");
  85.     Serial.print(time[5]);
  86.     Serial.print("/");
  87.     Serial.print(time[4]);
  88.     Serial.print(" ");
  89.     Serial.print(day[time[3]]);
  90.     Serial.print(" ");
  91.     Serial.print(time[2]);
  92.     Serial.print(":");
  93.     Serial.print(time[1]);
  94.     Serial.print(":");
  95.     Serial.println(time[0]);

  96.     brightness = analogRead(A6);
  97.     Serial.println(brightness);
  98.     duration = 1024 - brightness;//简单将亮度转换为数码管持续点亮时间
  99.     if (duration > 700) duration = 700;//过亮瞎眼

  100.     if (time[0] == 0) dht11_mark = 1;//初始化标志位
  101.     if (time[1] == 0 && time[0] == 0) buzzer_mark = 1;
  102.     if (time[1] == 30 && time[0] == 0) buzzer_mark = 1;

  103.     if (Serial.available())////随便输入什么来进入调时程序(调时时数码管不显示)
  104.     {
  105.       if(Command()==1)//返回1代表修改时间成功
  106.       {
  107.          tone(buzzer_pin, 3000, 100);//响铃
  108.          delay(150);
  109.          tone(buzzer_pin, 3000, 100);
  110.       }
  111.     }
  112.   }



  113.   if (time[1] == 0 && buzzer_mark > 0)//整点报时响两下
  114.   {
  115.     if (buzzer_mark == 1)
  116.     {
  117.       tone(buzzer_pin, 3000, 100);
  118.       buzzer_timemark = millis();
  119.       buzzer_mark = 2;
  120.     }
  121.     if (buzzer_mark == 2 && millis() - buzzer_timemark >= 150)
  122.     {
  123.       tone(buzzer_pin, 3000, 100);
  124.       buzzer_timemark = millis();
  125.       buzzer_mark = 0;
  126.     }
  127.   }

  128.   if (time[1] == 30 && buzzer_mark == 1)//30分响铃一下
  129.   {
  130.     tone(buzzer_pin, 3000, 100);
  131.     buzzer_timemark = millis();
  132.     buzzer_mark = 0;
  133.   }

  134.   if (time[0] == 5 && dht11_mark > 0)//每分钟第5秒收集温湿度数据
  135.   {
  136.     if (dht11_mark == 1)
  137.     {
  138.       pinMode(dht11_pin, OUTPUT);
  139.       digitalWrite(dht11_pin, LOW);
  140.       dht11_timemark = millis();
  141.       dht11_mark = 2;
  142.     }

  143.     if (millis() - dht11_timemark >= 18)//相当于长延时
  144.     {
  145.       GetTemp();
  146.       dht11_mark = 0;
  147.     }
  148.   }

  149.   if (millis() - systime >= 10 || millis() - systime < 0)//大约每10毫秒扫描一次数码管
  150.   {
  151.     systime = millis();

  152.     if (time[0] >= 11 && time[0] <= 12)//显示温度
  153.     {
  154.       Display(num[temperature / 10], dpin[3], duration);
  155.       Display(num[temperature % 10], dpin[2], duration);
  156.       //摄氏度符号
  157.       Display(B11000110, dpin[1], duration);//o
  158.       Display(B10011100, dpin[0], duration);//C
  159.     }

  160.     else if (time[0] >= 13 && time[0] <= 14)//显示湿度
  161.     {
  162.       Display(num[humidity / 10], dpin[3], duration);
  163.       Display(num[humidity % 10], dpin[2], duration);
  164.       //因数码管显示不了%,所以用rh(relative humidity相对湿度)来表示湿度
  165.       Display(B00001010, dpin[1], duration);//r
  166.       Display(B00101110, dpin[0], duration);//h
  167.     }

  168.     else
  169.     {
  170.       for (int i = 3; i >= 0; i--)
  171.       {
  172.         Display(dvalue[i], dpin[i], duration);
  173.       }
  174.     }

  175.   }

  176. }
复制代码


欢迎大家对我的作品做评价,特别是程序设计思想方面,好让我能继续进步;)
回复

使用道具 举报

 楼主| 发表于 2015-7-14 17:15:26 | 显示全部楼层
沙发自占自占自占
回复 支持 反对

使用道具 举报

发表于 2016-1-7 15:00:54 | 显示全部楼层
他xx的,不会做,先抄袭一个先,楼主是新人吗?我膜拜一下
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-5-10 23:33:43 | 显示全部楼层
半年下来,边玩边学,收益很大。
今天用了一天的时间改进了时钟的程序,更新如下:
*增加菜单操作
*用宏表示常量
*用结构体储存时间信息

程序在这里:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复 支持 反对

使用道具 举报

发表于 2023-4-14 09:48:10 | 显示全部楼层
有没有原理图,好玩,我也想复刻一个
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-4-25 09:45 , Processed in 0.052238 second(s), 20 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表