打印
[创新制造展示]

【21ic第三届设计大赛】+数字函数发生器

[复制链接]
4629|25
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 enderman1 于 2019-5-22 10:55 编辑

写在前面的话:
第一次参加论坛里的比赛,相对自己的能力进行一个检验,请大家多多关照~听说比赛截止时间是下月月底?本人决定慢慢来(虽然现在程序都写的差不多了),所以准备一天更新一次吧~(或许。。。)
本次设计采用硬件有
主控:    STM32F410     主要原因是本人手头正好有个STM32F410  nucleo的官方板卡(自带ST_link)美滋滋~
示波器: 蹭实验室的~
三极管若干:可能考虑到放大产生的信号
其它:....
思路:
使用stm32  DAC功能产生各种信号    (0~3v)

先来一个板卡的靓图~~



关于这次设计我准备分为三大部分来实现:



第一部分:理论分析与方案构想



第二部分:基本功能实现与论证



第三部分:代码优化与功能扩展



额外部分:作品展示与总结+视频演示



【第一部分】


一、实现方案分析

1、大概分析:首先拿到这个题目,要求要用MCU产生各种波形(线性波形)。要了解一个道理:MCU现在采用的是stm32,它是一个离散(数字)的系统(类比计算机),若要让他产生一个模拟信号,由于单片机拥有自身的工作频率,因此每次产生的信号理想状态都是冲击函数δ(t-τ)(序列)与想要产生的波形f(t-τ)的乘积,如图所示:(忽略垃圾字...)

实际情况下相当于对t时刻进行平顶抽样之后产生的那个值(也可以看作一个很窄门函数平移的结果),如图:

而目的是产生一个线性的模拟信号,因此当取样周期τ趋近于无穷小的时候,才可以达到理想状态,当然对于单片机来说(对于一个离散的数字系统而言)是不可能的,因此要尽量减少产生信号的周期;当然就算再减少,在示波器也可以看到阶梯状的信号~如图:

    --------------------放大~-----------------》   



2、难点分析:这个题目的难点个人认为是产生高斯噪声,主要原因是噪声是随机的,一个离散的系统(计算机,单片机等)无法产生一个真正的随机数,因为srand()里放的种子一般是时间或是与时间相关的值,都是有规律可循的,因此很难产生一个真正的随机数。当然解决办法也是有的,我会在后面提到。但是我又审了一遍题突然又发现原来是周期高斯函数。。。。。。。波形是这样的:

当然,还有就是自定义信号,可能会比较难设计。。。

3、题目分析:产生正弦信号,三角波,锯齿波等等,都可以用公式来实现;信号叠加功能可以直接叠加两个产生信号的函数相加即可;总而言之,主要是实现它们的算法。

4、实现思路分析:配置一个固定频率的dac产生信号,也就是说门函数是固定的,这也带来一个弊端:产生的信号频率越高,越失真(说白了就是越容易变成阶梯状的函数),好处就是容易写~   请看下图:

                                       


显而易见,频率大的信号在一个周期之内所遍历的梳状函数更少。。




二、硬件设计分析

设计框图:




1、说实话,这个题目涉及的硬件设计部分并不多,最多也就是用洞洞板或是画PCB电路图制作一个板卡的转接板,转接板上可以搞一些按键,和OLED显示转接口;按键来控制输出信号的参数,OLED来显示当前输出信号的参数。
2、我选择打板(听说现在打板很便宜??)我居然有个骚气的想法:这个转接板甚至连原理图都不用画,直接。。。

三、软件设计分析

说实话,快半年没有正儿八经的写32的程序了,于是我决定省点事儿,主要是跑思路。。。
软件设计用的keil 5     STM32CubeMX(自动生成初始化代码美滋滋)


总之今天就到这里了,其实今天已经吧功能写的差不多了;明天准备把今天写的代码完善一下。。。主要是晚上刚刚从实验室回来懒得拎示波器,所以也没法往下进行咯,今天先整理下思路,明天见~

使用特权

评论回复

相关帖子

沙发
enderman1|  楼主 | 2019-5-22 15:19 | 只看该作者
本帖最后由 enderman1 于 2019-5-22 22:22 编辑

【第二部分】基本功能实现与论证
一、硬件制作


其实老早就想给这个板卡弄一个PCB转接板了,但是又想引出更多的外设引脚,所以PCB板的话可能过几天才能弄好。。。因此为了快速的进行方**证的实现,我决定还是用万用板焊接一个简单的按键电路吧~

从垃圾里面翻出来的万用板(还是残缺的)凑合用吧~

焊完之后大概是这个样子:(焊接的不好请理解哈。。)



然后插到上面,没想到还像模像样。。。(之后我会添加上OLED显示)





二、接下来就是程序设计部分了,基本思路就是这个样子:



之后是程序的初始化,用了4个IO口作为按键输入,2个IIC,一个ADC,一个DAC;定时器中断频率为100KHZ (决定着冲击信号的频率);总之用cubeMX生成的了~(可能以后还要添加其它功能。。)



1、控制方法:
主要是配置4个按键,SW SE  +   -
SW是模式选择,选择发生的信号
SE是设置按键,对发生的信号进行设置(设置频率或者幅度)
+ -按键负责对频率或者幅度进行调节

2、信号发生函数
当前写了5个信号发生函数,分别为锯齿波,方波,正弦波,噪声,圆形波

值得一提的是,圆形波的幅度决定着它的频率因此可不用调频;而噪声是通过ADC采集外界噪声信号之后作为srand函数的种子,之后再生成的0-3V的噪声。。。(绕了一大圈~)
信号发生函数的定义:



主要实现思路是每次进入中断,调用一次产生信号的函数,而这个每一次可以可以表示时间(dx)而相对应的(dy)表示每次的增量,而这个增量是用函数f(x)=y来决定的;而到一定周期T之后x再恢复到0,如此重复。。。
而周期T是由用户设置的频率F来决定的,首先,抽样序列的周期是T1,而用户设定的频率决定着T,也间接的决定了每个周期内的抽样序列的多少。。。当频率足够大时,一个周期内的抽样信号过少,产生的信号也就不会平滑了(前面有提到过)
如图所示:


函数具体实现:(三角函数)


其它的实现方式也和这个差不多。。


3、验证:
各个波形的实现:

方波:


正弦波:


噪声:


ps.串口接收到的随机信号:
圆形波:


锯齿波:


三角波:


之后我突然发现一个问题:我必须要添加一个OLED显示屏来显示输出信号的理论参数了(好吧,快递正在路上。。)

PS.现在的代码还仅仅只实现了按键操作控制信号输出的功能,相对来说还不够完善(可能会出洋相),待本帖完结后我会上传完整版源代码~

4、OLED显示参数

嗯,没错,从某垃圾堆里淘来了一个OLED屏幕,欣喜若狂,于是就写了一个简单的参数显示:


在此之前,我遇到了一些小问题;主要是OLED显示的相关功能是在主循环里,而按键也在主循环里,所以就导致了按键响应不灵敏的问题;为了解决这个问题,我又不得不又开了一个定时器中断,中断优先级次于信号发生的中断优先级。中断周期为10ms,然后将按键扫描的函数塞到定时器中断里之后才算解决了这个问题。。。。

关于这个简单的界面,主要是对当前按键设计参数的显示,S按键对MOD_V进行选择(每个数字代表一种波形,之后我会改成字符串),SW是选择设置range和fre  (会有‘-->’)的标志;+ -  就不多说了吧。。。。

之后的工作:
①FLASH存储自定义波形并且输出
②通过串口接收的数据控制信号
③信号相加了

因此在之后的时间里我会将这些功能全部添加进来,今天就到这里吧,再见~


使用特权

评论回复
板凳
nvjwiciw659| | 2019-5-22 15:29 | 只看该作者
楼主楼主帅帅,我们我们爱爱

使用特权

评论回复
评论
enderman1 2019-5-22 15:33 回复TA
== 
地板
zhanzr21| | 2019-5-23 19:20 | 只看该作者
加油!
信号波形还有点缺陷,锯齿波, 三角波都有点"残",  另外信号不仅仅可以叠加, 还可以相减, 还可以相乘. 还有时间,精益求精, 冲击一等奖!

使用特权

评论回复
评论
enderman1 2019-5-23 19:43 回复TA
嗯嗯,加油! 
5
yppic82| | 2019-5-24 10:52 | 只看该作者
向楼主学习

使用特权

评论回复
6
enderman1|  楼主 | 2019-5-25 18:34 | 只看该作者
本帖最后由 enderman1 于 2019-5-29 16:54 编辑

三、串口指令的接收与处理

串口指令操作大大的提高了系统的操作灵活性,是除按键操作波形的另一个方法。在本系统中串口不仅仅要接收数据,也要发送数据以返回调试所需信息。


1、首先配置串口的相关参数,为了接收的实时性打开串口中断是一个不错的选择
      ST官方的nucleo板卡是ST-LINK上模拟的串口,连接的是主控板上的串口2,观察原理图可得知。。



     之后就是cubeMX的各种配置。。考虑到发生抽样的定时器优先级最高,串口中断的优先级需低于它。


接下来打开串口中断,值得注意的是HAL库使用串口中断时(若是串口接收中断)在初始化之后要启用接收数据寄存器非空中断。
__HAL_UART_ENABLE_IT(&huart2,UART_IT_RXNE);
或者先调用一次
HAL_UART_Receive_IT。
这个函数(因为这个函数里包含了启用中断);
2、自定义指令

由于接受的是字符串而不是单个字符,因此需要一个字符数组来作为存储缓冲器。另外,定义用‘#’作为一个指令的结束符号;

具体是在中断服务程序里实现的。
HAL_UART_Receive_IT(&huart2,(uint8_t *)&uart_point,1);
        uart_read_dat[i++]=uart_point;
        while(HAL_UART_Transmit(&huart2, (uint8_t*)uart_read_dat,i, 5000)!= HAL_OK);
        i=0;
        if(uart_point=='\n')read_flag=1;
list=0;

另外,在使用printf()这个函数时,需要重新定向fputc这个函数
int fputc(int ch, FILE *f)
{
  HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xffff);
  return ch;
}
并且要把这个勾上


之后再结合逻辑上对接收的字符串的判断,就可以对接收到的字符串/指令 进行操作了。

对数据的处理:(S开头是信号,F开头频率,A开头是幅度)
<div class="blockcode"><blockquote>if(read_flag==1){
                        while(uart_read_dat[list++]!='#'&&list<16){
                                        read_flag=0;
                                        if(uart_read_dat[list=='S']){
                                                        sign_flag=1;
                                                        fre_cmd_flag=0;
                                                        range_cmd_flag=0;
                                        }
                                        if(uart_read_dat[list=='F']){
                                                        fre_cmd_flag=1;
                                                        range_cmd_flag=0;
                                                        sign_flag=0;
                                                        j=0;
                                        }
                                        if(uart_read_dat[list=='A']){
                                                        range_cmd_flag=1;
                                                        fre_cmd_flag=0;
                                                        sign_flag=0;
                                                        j=0;
                                        }
                                        if(fre_cmd_flag){                                                        //频率
                                                        if(uart_read_dat[list]==' ')continue;
                                                        setting_fre=setting_fre*10+asc_to_val(uart_read_dat[list++]);
                                        }
                                        if(range_cmd_flag){                                                //幅度
                                                        if(uart_read_dat[list]==' ')continue;
                                                        setting_range=setting_range*10+asc_to_val(uart_read_dat[list++]);
                                        }
                                        if(sign_flag){
                                                        if(uart_read_dat[list]==' ')continue;
                                                        sign_mod_char[j++]=uart_read_dat[list++];
                                        }
                        }
                }


ascii码数字转数值:
unsigned char asc_to_val(unsigned char asc)
{
uint8_t valu;
switch(asc)
{
case 0x30:valu=0;break;
case 0x31:valu=1;break;
case 0x32:valu=2;break;
case 0x33:valu=3;break;
case 0x34:valu=4;break;
case 0x35:valu=5;break;
case 0x36:valu=6;break;
case 0x37:valu=7;break;
case 0x38:valu=8;break;
case 0x39:valu=9;break;
}
return valu;
}
之后发送一个形如“Ssin F1020 A25”的指令就可以了

3、效果如下:





PS.OLED显示 幅度多了一个2,推断是显示处理的问题吧~

检查了一下,果然如此,将显示幅度数组改成2即可(可能是定义%lf类型之后小数点的字符并不占用定义的数组空间)

      





所以,操作了这么多,其实和信号没有扯上多大关系,大多都是字符串的操作;之所以这几天没有更新是因为近期乱七八糟的事情有点多,而且考虑到信号的叠加等等的操作(诸如3+3V)这种超出stm32   DAC输出范围的操作可能需要运算放大器或者是三极管,并且有判断是否大于3V的功能,不仅如此,还要考虑信号是否失真等等因素; 另外  对于较高频信号输出不平滑这个问题,软件硬件都可以解决(目前偏向于后者)。

综上考虑,可能之后还要对硬件以及软件设计进行升级。因此计划之后发布内容会根据上述问题为中心展开讨论与实践。那么这一次就更新这么多吧,再见~





(最近同学们都在努力学习啊,本人压力好大~     吼吼!)




使用特权

评论回复
7
一路向北lm| | 2019-5-26 21:15 | 只看该作者
enderman1 发表于 2019-5-25 18:34
三、串口指令的接收与处理

串口指令操作大大的提高了系统的操作灵活性,是除按键操作波形的另一个方法。在 ...

楼主很棒啊,做的不错

使用特权

评论回复
8
zhanzr21| | 2019-5-27 22:25 | 只看该作者
enderman1 发表于 2019-5-25 18:34
三、串口指令的接收与处理

串口指令操作大大的提高了系统的操作灵活性,是除按键操作波形的另一个方法。在 ...

小兄弟, 做得不错, 就是代码方面还有点提升的余地.
比如这个函数:
    unsigned char asc_to_val(unsigned char asc)
    {
    uint8_t valu;
    switch(asc)
    {
    case 0x30:valu=0;break;
    case 0x31:valu=1;break;
    case 0x32:valu=2;break;
    case 0x33:valu=3;break;
    case 0x34:valu=4;break;
    case 0x35:valu=5;break;
    case 0x36:valu=6;break;
    case 0x37:valu=7;break;
    case 0x38:valu=8;break;
    case 0x39:valu=9;break;
    }
    return valu;
    }


可否直接改为:
    #define ASC_TO_DIG(asc) ((unsigned char)(asc-(unsigned char)'0'))

纯技术讨论啊, 加油!

使用特权

评论回复
评论
enderman1 2019-5-28 22:34 回复TA
非常感谢!!! :-) 
enderman1 2019-5-28 22:31 回复TA
确实如此啊,我会在第三阶段给来个总体的代码优化~ 
9
enderman1|  楼主 | 2019-5-28 22:33 | 只看该作者
本帖最后由 enderman1 于 2019-12-16 11:14 编辑

玩了一晌的运算放大器(最终玩的结果是验证了课本上都是对的),好,明天更新!
ps.计划明天更新的内容是信号的叠加(包括相乘等),要用到运放对超过3V的信号进行输出;当然,用于按键也就这么多了,准备用串口指令控制

使用特权

评论回复
10
enderman1|  楼主 | 2019-5-29 16:49 | 只看该作者
本帖最后由 enderman1 于 2019-5-29 19:08 编辑

四、串口指令_加强版(四则运算)

之前忽略了一个重要的点,就是串口指令的处理;虽然现在通过修改程序已经可以实现信号的四则运算,但是如何利用输入的串口指令进行四则运算是重点,这牵扯到了C语言中的数组操作部分。正好我利用了中午的时间完成了这个任务,并且总结了一些经验。
另外,这其实是一个独立的程序,后期用于移植到信号发生器上使用

PS.虽然完成了,但是我并不感觉我的实现方法多么的高明,所以如果有漏洞或者更好的思路的话,欢迎dalao们提出意见或建议~

1、自定指令

首先,指令的格式是   xxx (+、-、*、/)  yyy  ,xxx代表的是一个信号,yyy代表的是另一个信号;例如 sin*ang(表示正弦信号×三角波);当然算出的结果仅仅是形状正确,理论上经过放大之后就是得到的正确信号(只用乘上或是除以一个固定比例值即可),当然,前提是信号不失真。


在这里,我定义了几个信号所表示的符号(指令):


1、sin              (正弦信号)
2、ang             (三角波)
3、saw           (锯齿波)
4、squ            (方波)
5、cir                (半圆波)
6、non              (什么也没有)

2、串口的一些操作


这一次某些地方用了寄存器的操作(主要是用寄存器心里踏实


1、首先是中断,在进入while(1)循环前,一定要先调用这个,目的是开启  读数据寄存器非空  中断


__HAL_UART_ENABLE_IT(&huart2,UART_IT_RXNE);




3、中断服务程序


(用cubeMX的话,照网上的说法其实这样写并不标准,但是其实是可以用的,目前没有出现任何问题)


read_flag=1;
作为接受之后的一个标志位,当然在这里特地设定的为7个(标准的做法是判断接受非空)

现在是需要每个指令+回车才可以

<div class="blockcode"><blockquote>void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */

  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_IRQn 1 */
        uart_read_dat=USART2->DR&0XFF;
        uart_read_buf[list]=uart_read_dat;
        list++;
        if(uart_read_dat=='\n'){
                        list=0;
                        read_flag=1;
        }
        __HAL_UART_ENABLE_IT(&huart2,UART_IT_RXNE);
        //printf("%d\n",list);
  /* USER CODE END USART2_IRQn 1 */
}



4、指令的判断


其实就是循环对每个字符进行判断,逻辑比较简单


//start_point  是开始数组下标  只对比3个字符
unsigned char std_str_con(unsigned char *str,unsigned char start_point){
                unsigned char i,j,count,out_dat;
                count=0;
                out_dat=0;
                for(i=0;i<7;i++){
                                count=0;
                                for(j=start_point;j<3+start_point;j++){
                                                if(str[j]==signal_class[i][j-start_point])count++;
                                }
                                if(count==3){
                                                out_dat=i+1;
                                                break;
                                }
                }
                return out_dat;
}




5、运算


unsigned char scan_str_con(unsigned char *str){
                unsigned char first_num,second_num,return_num;
                first_num=std_str_con(str,0);
                second_num=std_str_con(str,4);
                if(first_num>0&&first_num<8){
                                switch(str[3]){
                                        case '+':return_num=first_num+second_num;break;
                                        case '-':return_num=first_num-second_num;break;
                                        case '*':return_num=first_num*second_num;break;
                                        case '/':return_num=first_num/second_num;break;
                                        default :return_num=0;break;
                                }
                }else{
                                return_num=0;
                }
                return return_num;
}



6、主函数里的循环


while (1)
  {
               
                if(read_flag)                                                                //接收数据完毕
                {
                        read_flag=0;
                        read_command=scan_str_con(uart_read_buf);
                        if(read_command==0){                        //非法指令
                                        printf("error command!\n");
                        }else{                                                                                //合法指令
                                        for(i=0;i<7;i++){
                                                printf("%c",uart_read_buf[i]);
                                        }
                                        printf("结果是:%d\n",read_command);
                        }
                        printf("\n");
                        
                }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }


7、执行效果


这里把每个指令字符串定义为了一个数进行运算,其实放到信号上也是同样的道理,只不过是信号代替了数值。如果指令正确,串口会返回一个计算过的数值,如果错误则显示“error”。





使用特权

评论回复
11
enderman1|  楼主 | 2019-5-31 16:48 | 只看该作者
本帖最后由 enderman1 于 2019-6-1 10:38 编辑

楼主要先停更几天了,原因大家都懂
可能要再过一周才能回来~

IMG20190529103015.jpg (136.57 KB )

IMG20190529103015.jpg

使用特权

评论回复
12
enderman1|  楼主 | 2019-6-16 22:24 | 只看该作者
从明天开始继续更新,接下来的任务主要是代码的优化和自定义波形的实现。

使用特权

评论回复
13
enderman1|  楼主 | 2019-6-17 18:26 | 只看该作者
本帖最后由 enderman1 于 2019-12-16 11:15 编辑

ps.终于回来了,我在想,如果这几天不把这个完成了,以后更没有时间了。
一、


1、我昨天提到过,之后的任务就是程序优化,但是这一构思优化思路才发现所有的东西都基本改变了。之前产生波形的方法不仅仅麻烦(因为靠的是函数和实现它的算法),而且牺牲了单片机的算力(基本上全部都是大数或是浮点运算)。所以之后在想怎么解决这个问题,近几天我的一位学长建议我用DMA搬移数据,之后我就开始行动了,因为我用的是hal库,里面的函数参数都是指针了什么的,而且我看的例程也是对数组的操作,这让我**一闪,我想到了一个高效信号产生的办法,将事先要发生的信号写入到数组里(当然都是数字信号),之后让定时器中断每中断一次送一个数组数据(DMA搬运数据),这样既完成了特定信号的发生,又给自定义信号提供了一个实现思路,简单的来说就是牺牲单片机的ROM来提高相应时间(速度),在此之前我有见过在51单片机上跑FFT的程序,而且执行的还不错,里面用的方法就是把事先用到的常数放到数组里,免去了单片机现场计算的时间,当时仅仅是了解了,但是并没有深刻的意识去应用,而自此之后我可能在编程或是写算法当中就要注意这一点了。


2、刚刚才看见有人已经用这个方法了,那行吧(人家才是真正的dalao)

使用特权

评论回复
14
奋斗小范| | 2019-6-17 20:35 | 只看该作者
为啥没有看到用FPGA做这个的呢

使用特权

评论回复
评论
enderman1 2019-6-19 15:51 回复TA
不会FPGA啊,QAQ 
15
enderman1|  楼主 | 2019-6-19 15:54 | 只看该作者
本帖最后由 enderman1 于 2019-6-19 16:52 编辑

用一张照片证明我这两天没有在偷懒。。。
PS.其实就是懒得去实验室用示波器了,本着自给自总丰衣足食的原则,自己用STM32F429做了一个简易示波器(那么问题来了,我为什么不用这个F429的板卡即当函数信号发射器用,又当示波器呢?可能是太懒了)凑合用吧,随后我会发一下代码~


使用特权

评论回复
16
enderman1|  楼主 | 2019-6-30 14:46 | 只看该作者
本帖最后由 enderman1 于 2019-6-30 14:47 编辑

最后的工作:各种波形的(加法)运算+总结

之前忘记写这个ID了,现在补上。


锯齿+正弦波

方波+正弦波

三角波+锯齿波

方波+锯齿波

目前就用串口指令搞了这么多,而且并没有验证其正确性,而且关于乘法除法等涉及到幅度比例问题,还是比较复杂的。本来在一个月之内还是想做很多的,但是理想与现实还是比较有差距的,这也让我明白了一个道理,把一个东西做出来很容易,但是要真正把它做好实属不易。它涉及到了你之前学习的能力和功底,就比如说做这个东西之前我最先想到的是用stm32生成现成的波形,虽然这个方法也不是错误的,但是太过理性化以至于忽视了运算所花费的时间。相反,我的那位学长(一路向北lm)却想到的是事先把波形存储到ROM,通过DMA来搬运数据DA转换,这样做不仅算法简便,而且更加高效,在之后的时间内我也尝试了一下,确实很棒,不仅如此,他还利用了他之前学到的知识对这个设计进行逐步改甚至添加了更多的高端功能,这就是水平高低的本质体现,通过这次比赛,我见到了包括学长在内的很多高手,对此我想说的是我的水平还是不行,今后的日子里需要更加努力的学习啊!

PS.本来还想拍个视频来着,因为前段时间期末考试,也没有拍,如果能补的话我一定会补上的,所以本帖到此也就接近尾声了,总之希望能得个奖吧,嘿嘿~

本来想发下代码,结果超过5M发不了,我再另谋它法吧。。

使用特权

评论回复
评论
enderman1 2019-7-21 11:15 回复TA
@zhanzr21 :谢谢建议~ :) 
zhanzr21 2019-6-30 22:11 回复TA
做得很不错了, 总结也很好. 行动+反思+再行动+再反思. 如此循环, 一定能成为高手.不要纠结于DMA这种特定技术, DMA是可以提高速度, 但是设计目标不是一味求快, 而是在计算的基础上获得灵活性, 如果使用了DMA输出预先存储的波形, 那么波形的运算怎么做到灵活呢. 
17
OTG326| | 2019-7-9 21:04 | 只看该作者
谢谢分享,谢谢分享!

使用特权

评论回复
18
character| | 2020-9-12 16:52 | 只看该作者
不错哦

使用特权

评论回复
19
character| | 2020-9-17 08:16 | 只看该作者
不错

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

11

主题

71

帖子

12

粉丝