基于STM32+RC522设计的门禁系统

安全设备/系统

153人已加入

描述

一、项目背景

门禁系统是现代社会中非常重要的安全控制系统之一,其功能是在保障建筑物安全的同时,为合法用户提供便利。当前设计一种基于STM32+RC522的门禁系统设计方案,通过RFID-RC522模块实现了对用户卡的注册、识别及身份验证,通过控制SG90舵机实现门锁的开关,具有较高的安全性和可靠性。实验结果表明,该门禁系统可以有效地保障建筑物的安全性。

门禁系统广泛应用于各种建筑物、企事业单位,用于管理人员的进出、控制人员活动范围、实现安全监控等功能。传统的门禁系统采用密码输入或刷卡的方式进行身份验证,但存在易被破解的风险。基于RFID的门禁系统已经成为一种相对先进的安全控制方案。

本次设计的STM32+RC522门禁系统,通过RFID-RC522模块对用户的卡进行注册、识别完成身份识别,对门锁进行开关。系统带了OLED显示屏,输入用户密码登录之后,可以对新卡片进行注册,添加新卡片,对不使用的卡片进行注销。在系统里,IC卡的数据都存储在卡的内部扇区里,通过卡的内部空间进行管理。

采用5V-步进电机的版本:

STM32

二、系统设计

门禁系统由STM32F103C8T6单片机、RFID-RC522模块、SG90舵机、LCD1602液晶显示屏、键盘模块等组成。其中,STM32F103C8T6单片机作为系统的核心控制器,控制程序的执行;RFID-RC522模块作为识别用户卡片的设备;SG90舵机作为门锁控制设备;OLED显示屏提供用户输入信息和系统信息的显示;键盘模块方便用户进行密码和卡片信息的输入。

2.1 软件设计

【1】RFID卡信息管理

本系统采用卡的内部空间进行IC卡信息的管理。每个IC卡可以分为多个扇区,每个扇区包含多个块,每个块包含16个字节。扇区0是厂家已经预留好的,用于存储卡片的序列号,扇区1-15可以由用户自己配置,用于存储一些私有数据,如用户身份、车牌号、员工编号等。

在本系统中,IC卡信息的管理主要包括三个方面:新卡片注册、卡片识别和注销卡片。

对于新卡片的注册,用户需要按下键盘上的“#”键进入注册模式,接着输入管理员密码,然后将新卡放到RFID读写器上,系统将读取卡片序列号,并在卡片的扇区中存储用户名和密码信息等。

对于卡片的识别,当用户按下门禁系统的确认键时,系统将读取RFID模块中读取的卡片序列号,并去卡片扇区中查询用户名和密码信息,进行身份验证。如果卡片识别成功,系统将控制舵机旋转一圈实现开锁功能。

对于注销卡片,管理员需要输入密码进行身份验证后,再将要注销的卡片放到RFID读写器上,系统将清空该卡片的扇区内所有数据。

【2】门禁系统安全控制

本门禁系统采用密码验证和卡片识别相结合的方式,提高了系统的安全性。具体来说,系统要求用户输入密码或刷卡进行身份验证,只有在验证成功后才能控制门锁进行开关操作。同时,系统还可以记录每一次开启门锁的时间和用户信息,以便管理员进行安全监控。

【3】门锁控制

本门禁系统采用SG90舵机控制门锁的开关,具有结构简单,控制方便的优点。在门锁控制过程中,系统对舵机控制信号的频率和占空比进行精细控制,以实现门锁的准确开关。

2.2 硬件设计

【1】STM32F103C8T6单片机

STM32F103C8T6单片机是ST公司推出的一款基于Cortex-M3内核的可编程32位单片机,常常被广泛应用于工业控制、智能家居、嵌入式控制等领域。

STM32

它的主要特点包括:

 

 1. Cortex-M3内核:STM32F103C8T6使用Cortex-M3内核,具有高性能、低功耗、硬实时等特点,可支持多个串口、I2C、SPI、USB等外设,为使用者带来更大的灵活性。
 2. 32位处理能力:STM32F103C8T6是一款32位单片机,具有比8位、16位单片机更高的数据运算能力、编程灵活度和计算精度。
 3. 较强的系统时间管理能力:STM32F103C8T6内部具备RTC实时时钟模块,可实现精准的时间管理和时间标记功能,在一些需要时间同步的应用场景下具有较大的优势。
 4. 大存储容量:STM32F103C8T6内置64K字节的闪存和20K字节的SRAM,能够满足大型嵌入式应用的存储需求。
 5. 丰富的外设接口:STM32F103C8T6支持多个外设接口,如SPI、I2C、CAN总线等,方便开发者扩展相关应用场景。
 6. 代码可移植性强:由于该芯片应用广泛,可以使用多种开发工具进行开发,例如Keil、STM32CubeMX等,而且支持多种编程语言,如C语言、C++等,因此优点很容易在不同的平台、不同开发者之间实现代码的移植。

 

【2】RFID-RC522模块

RFID-RC522模块是一种低成本、高性价比的RFID读写模块。它具有高精度、快速读取等特点,广泛应用于门禁系统、智能卡管理、物流追踪等领域。

STM32

RFID-RC522模块的特点如下:

 

 1. 高精度:RFID-RC522模块采用射频感应技术进行信号传输和读写,具有高精度、稳定性强等优点。
 2. 快速读取:RFID-RC522模块读取速度快,一般只需0.1秒左右就可以完成读取操作。
 3. 支持多种协议:RFID-RC522模块支持ISO14443A/B、FeliCa等多种RFID协议,可满足不同应用场合的需求。
 4. 低功耗:RFID-RC522模块功耗低,工作电流为13-26mA,待机电流为10A。
 5. 接口简单:RFID-RC522模块采用SPI接口进行通信,模块上的引脚有7个,具有很好的兼容性。
 6. 支持多种开发语言:RFID-RC522模块支持多种开发语言,如C++、Python等,方便开发者进行二次开发。

 

RFID-RC522模块的使用需要配合相关的库文件,在Arduino、Raspberry Pi等开发板上进行代码编写和开发。常见的使用场景包括门禁系统、智能卡管理、出入库管理、物流追踪等领域。

【3】SG90舵机

该舵机小巧耐用,可以精确地控制门锁的开关。

SG90舵机是一种小型舵机,体积小、重量轻、价格低廉,常常被用于模型飞机、小型机械臂、玩具模型等领域。它采用了直流电机,利用PID控制技术,以及精密的小齿轮减速箱实现转向角的控制。

STM32

SG90舵机的特点如下:

 

1. 采用IIC总线通信:IIC接口的4x4电容矩阵键盘模块通过IIC总线通信连接到MCU,简化了连接方式,方便使用。
2. 采用电容式按键设计:每个按键上放置一个电容器,当手指触摸到按键时,电容器的电容值发生变化,通过检测电容的变化实现按键检测。
3. 4x4矩阵排列式设计:4x4电容矩阵键盘模块采用矩阵排列式设计,一共有16个按键,可以满足较为复杂的应用场景。
4. 接口简单:IIC接口的4x4电容矩阵键盘模块只需要SCL和SDA两条线连接到MCU即可。
5. 高灵敏度:电容式按键设计使得按键检测更加灵敏,而且不会产生按键轻微弹起的误触情况,使用更加舒适。
6. 代码简洁:使用该模块并不需要编写复杂的按键扫描程序,只需要通过读取IIC总线上的按键值即可。

 

SG90舵机在使用时需要通过PWM信号进行控制。

【4】0.96寸OLED显示屏

0.96寸SPI接口OLED显示屏是一种小型化的屏幕,属于OLED显示技术,采用SPI接口连接,外观尺寸约为12mm * 12mm,分辨率一般为128 * 64或者128 * 32。它可以用于各种小型电子设备,例如手持设备、小型仪器、智能家居控制面板等等。

OLED即有机发光二极管,与传统的液晶显示屏相比,OLED具有响应速度快、视角范围广、色彩鲜艳、亮度高等优势。SPI接口则是一种串行外设接口,具有简单、灵活、高速等特点。

STM32

0.96寸SPI接口OLED显示屏的驱动芯片一般为SSD1306,有128个列和64个行的像素,还有一些有128个列和32个行的像素。其中,128 * 64像素的屏幕显示面积较大,在显示图像和文字时更加清晰和细腻。0.96寸SPI接口OLED显示屏具有小巧、高清、高速等优点,被广泛使用在各种小型电子设备中。

【5】键盘模块

该模块可以方便地输入密码和卡片信息。

IIC接口的4x4电容矩阵键盘模块是一种基于IIC总线通信的电容式按键模块,常常被应用在工控、家电、医疗器械等领域。

STM32

它的主要特点包括:

 1. 采用IIC总线通信:IIC接口的4x4电容矩阵键盘模块通过IIC总线通信连接到MCU,简化了连接方式,方便使用。  2. 采用电容式按键设计:每个按键上放置一个电容器,当手指触摸到按键时,电容器的电容值发生变化,通过检测电容的变化实现按键检测。  3. 4x4矩阵排列式设计:4x4电容矩阵键盘模块采用矩阵排列式设计,一共有16个按键,可以满足较为复杂的应用场景。  4. 接口简单:IIC接口的4x4电容矩阵键盘模块只需要SCL和SDA两条线连接到MCU即可。  5. 高灵敏度:电容式按键设计使得按键检测更加灵敏,而且不会产生按键轻微弹起的误触情况,使用更加舒适。  6. 代码简洁:使用该模块并不需要编写复杂的按键扫描程序,只需要通过读取IIC总线上的按键值即可。

IIC接口的4x4电容矩阵键盘模块是一种方便易用、高灵敏度的按键模块,通过电容式按键设计实现按键的检测和响应,并且通过IIC总线通信简化了连接方式。它适合于应用于许多领域,如工控、家电和医疗器械等,能够为使用者的产品带来更为方便和高效的控制方式。

三、核心代码

3.1 SG90舵机控制代码

下面是基于GPIO模拟时序控制STM32F103C8T6驱动SG90舵机旋转指定的角度的代码,并封装成子函数调用。

 

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "delay.h"
​
#define Servo_pin  GPIO_Pin_5
#define Servo_port GPIOA
​
void SG90_Init(void)
{
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
​
 GPIO_InitTypeDef GPIO_InitStructure;
 GPIO_InitStructure.GPIO_Pin = Servo_pin;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_Init(Servo_port, &GPIO_InitStructure);
}
​
void SG90_SetAngle(uint8_t angle)
{
 if(angle>180) angle=180;
 if(angle<0) angle = 0;
 ​
   uint8_t temp = angle/2 + 15;
 ​
   for(int i=0;i<5;i++)
   {
     GPIO_SetBits(Servo_port, Servo_pin);
     delay_us(temp);
     GPIO_ResetBits(Servo_port, Servo_pin);
     delay_us(20000-temp);
   }
 }
 ​
 int main(void)
 {
   SystemInit();
 ​
   delay_init();
 ​
   SG90_Init();
 ​
   while(1)
   {
     for(int i=0;i<=180;i+=10)
     {
       SG90_SetAngle(i);
       delay_ms(500);
     }
   }
 }

 

其中,SG90_Init()函数用于初始化PA5口,并将其配置为输出模式。SG90_SetAngle()函数用于驱动舵机旋转到指定角度。在该函数中,首先根据所给的角度值计算出延时的时间temp(单位为微秒),然后使用GPIO口控制SG90舵机在temp延时时间内输出高电平,其余时间输出低电平。通过调整延时时间和按角度分配脉冲宽度,达到驱动SG90舵机旋转的目的。

main()函数中的for循环控制舵机从0度到180度的循环旋转。代码中用到了delay_init()函数和delay_ms()、delay_us()函数。它们是自行编写的延时函数,可以实现毫秒和微秒级别的延时,具体代码如下:

 

#include "stm32f10x.h"
​
void delay_init(void)
{
  if (SysTick_Config(SystemCoreClock / 1000000)){
    while(1);
   }
}
​
static __IO uint32_t delay_us_tick;
void delay_us(uint32_t nUs)
{
  delay_us_tick = nUs;
  while (delay_us_tick);
}
​
static __IO uint32_t delay_ms_tick;
void delay_ms(uint32_t nMs)
{
  delay_ms_tick = nMs;
  while (delay_ms_tick);
}
​
void SysTick_Handler(void)
{
  if (delay_us_tick > 0){
    delay_us_tick--;
   }
​
  if (delay_ms_tick > 0){
    delay_ms_tick--;
   }
}

 

其中,delay_init()函数用于配置系统时钟源和SysTick定时器,实现每个SysTick时钟产生一个中断的功能。delay_us()函数和delay_ms()函数分别用于实现微秒级别和毫秒级别的延时,通过限制delay_us_tick和delay_ms_tick的值实现延时的效果。SysTick_Handler()为中断处理函数,每次SysTick定时器计数减1,当减到0时,相应的delay_us_tick或delay_ms_tick也减1,通过循环等待该值为0实现延时。

在代码中的SG90_SetAngle()函数中,需要精确控制GPIO的电平时间,使其产生相应的脉冲宽度,从而控制舵机转动角度。因此,需要配置GPIO口的输出模式和速度、设定delay_us()函数中根据角度计算的电平时间,使得舵机能够准确地执行旋转。

3.2 RC522读写代码

下面是基于SPI接口控制STM32F103C8T6驱动RFID-RC522模块完成卡片识别和扇区读写的代码示例。在该代码中,使用的是SPI1的接口,RFID-RC522模块通过SPI1接口连接到STM32F103C8T6。

代码中通过封装SPI相关操作和MFRC522库函数,实现了读取卡片信息和完成扇区读写的功能。

 

#include "stm32f10x.h"
#include "stm32f10x_spi.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "delay.h"
#include "mfrc522.h"
#include "stdio.h"

#define     SPI_CE_LOW()      GPIO_ResetBits(GPIOA,GPIO_Pin_4)
#define     SPI_CE_HIGH()     GPIO_SetBits(GPIOA,GPIO_Pin_4)

void SPI1_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;      
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    SPI_InitTypeDef SPI_InitStructure;
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_Init(SPI1, &SPI_InitStructure);

    SPI_Cmd(SPI1, ENABLE);
}

uint8_t SPI1_SendByte(uint8_t byte)
{
    while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
    SPI_I2S_SendData(SPI1, byte);

    while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) ==
RESET); return SPI_I2S_ReceiveData(SPI1); }

void MFRC522_Reset(void) { SPI_CE_LOW(); SPI1_SendByte(0x1B); SPI_CE_HIGH(); }

uint8_t MFRC522_ReadRegister(uint8_t addr) { SPI_CE_LOW(); uint8_t data; SPI1_SendByte(0x80 | addr); data = SPI1_SendByte(0x00); SPI_CE_HIGH(); return data; }

void MFRC522_WriteRegister(uint8_t addr, uint8_t val) { SPI_CE_LOW(); SPI1_SendByte(0x7F & addr); SPI1_SendByte(val); SPI_CE_HIGH(); }

void MFRC522_ReadRegisters(uint8_t addr, uint8_t count, uint8_t *values) { SPI_CE_LOW(); SPI1_SendByte(0x80 | addr); for(uint8_t i=0;i

 

测试。。。

详细流程:

1. 创建 RtcEngine 对象

该对象管理了整个的视频等场景,是一个非常核心的对象。

 

 try {
            RtcEngineConfig config = new RtcEngineConfig();
            config.mContext = getBaseContext();
            config.mAppId = appId;
            config.mEventHandler = mRtcEventHandler;
            config.mAudioScenario = Constants.AudioScenario.getValue(Constants.AudioScenario.DEFAULT);
            mRtcEngine = RtcEngine.create(config);
        } catch (Exception e) {
            throw new RuntimeException("Check the error.");
        }

 

2. 创建 RtcEngine 属性

 

  // 视频默认禁用,你需要调用 enableVideo 启用视频流。
        mRtcEngine.enableVideo();
        // 录音默认禁用,你需要调用 enableAudio 启用录音。
        mRtcEngine.enableAudio();
        // 开启本地视频预览。
        mRtcEngine.startPreview();

 

3. 将本地摄像头内容显示到 local_video_view_container 上

 

FrameLayout container = findViewById(R.id.local_video_view_container);
        // 创建一个 SurfaceView 对象,并将其作为 FrameLayout 的子对象。
        SurfaceView surfaceView = new SurfaceView (getBaseContext());
        container.addView(surfaceView);
        // 将 SurfaceView 对象传入声网,以渲染本地视频。
        mRtcEngine.setupLocalVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, KeyCenter.RTC_UID));

 

4. 设置当前的模式

 

   ChannelMediaOptions options = new ChannelMediaOptions();

        // 将用户角色设置为 BROADCASTER。
        options.clientRoleType = Constants.CLIENT_ROLE_BROADCASTER;
        // 视频通话场景下,设置频道场景为 BROADCASTING。
        options.channelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING;

 

其中 clientRoleType 有两种,如果是 CLIENT_ROLE_BROADCASTER 就是可以播和收,如果是 CLIENT_ROLE_AUDIENCE 就只能收看,当前就成了主播模式。

5. 加入频道

 

 // 使用临时 Token 加入频道。
        // 你需要自行指定用户 ID,并确保其在频道内的唯一性。
        int res = mRtcEngine.joinChannel(token, channelName, KeyCenter.RTC_UID, options);
        if (res != 0)
        {
            // Usually happens with invalid parameters
            // Error code description can be found at:
            // en: https://docs.agora.io/en/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_code.html
            // cn: https://docs.agora.io/cn/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_code.html
            Log.e("video","join err:"+RtcEngine.getErrorDescription(Math.abs(res)));

        }

 

joinChannel 方法有返回值,可以看到我们加入频道是否成功了,如果不成功的话,我们可以看下错误原因,并对照解决;如果成功了,就可以观察 IRtcEngineEventHandler 对象的回调方法,重点关注下 onError (int err) 和 onJoinChannelSuccess (String channel, int uid, int elapsed) 如果收到 onJoinChannelSuccess 方法的回调,我们就可以关注 onUserJoined (int uid, int elapsed) 方法,我们可以在这个方法里开始显示远端内容。

6. 显示远端内容

 

   @Override
        // 监听频道内的远端主播,获取主播的 uid 信息。
        public void onUserJoined(int uid, int elapsed) {
            Log.e(TAG, "onUserJoined->" + uid);
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    // 从 onUserJoined 回调获取 uid 后,调用 setupRemoteVideo,设置远端视频视图。
                    setupRemoteVideo(uid);
                }
            });
        }
  private void setupRemoteVideo(int uid) {
        FrameLayout container = findViewById(R.id.remote_video_view_container);
        SurfaceView surfaceView = new SurfaceView (getBaseContext());
        surfaceView.setZOrderMediaOverlay(true);
        container.addView(surfaceView);
        mRtcEngine.setupRemoteVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, uid));
    }

 

最终,远端的视频会显示在 remote_video_view_container 上。

1、接口遗漏

通过精准平台确认通过商家用户 id 查询退货地址接口也存在对应的改动,开发少梳理了一个,并同步开发在清单中进行补充。

STM32

2、代码分支合并

商家核心服务识别出了一个非地址相关的改动,跟开发确认之后发现是因为当前提测分支没有合并当前线上最新的 master 分支,导致跑出了非地址相关的功能数据。告知开发进行对应代码合并之后,QA 需要重新部署重新拉取接口再次确认。

STM32

3、接口重载

商家核心服务有一个重载方法精准平台没有识别出这个根据商家 id 查询商家地址的接口,跟开发进行确认,最终结论是根据商家 id 查询商家地址的 V2 接口是重载了以下接口:平台没有看到重载的方法名,最终确认重载方法还是原来的方法名,只是在入参上作区分,一个只有商家 ID,一个需要传商家 ID、订单类型、商品 ID。最终在精准平台确认了对应的入参区别,平台没有作为两个接口进行识别。

4、内部调用方法也被识别

在使用精准平台进行新增接口拉取的时候,发现平台会将一些私有方法识别出来,这些方法是内部调用,没有注册,接口测试平台也看不到对应的接口信息,无法覆盖。

STM32

3.6 最终接口确认

1、商家拆分服务

本次涉及 8 个改动接口,并补充缺少的 4 个接口的自动化之后,正常识别且状态正常,说明此服务接口都正常覆盖。

STM32STM32

2、商家核心服务

本次涉及 10 个接口改动,并补充缺少的接口自动化之后,对应接口都能正常识别且状态正常,说明此服务接口都正常覆盖。

STM32STM32

3、商家下单链路服务

本次涉及 4 个接口改动,并补充缺少的 2 个接口自动化之后,对应接口都能正常识别且状态正常,说明此服务接口都正常覆盖。

STM32STM32

四、结果

4.1 各个服务触发自动化结果

1、商家拆分服务

STM32STM32STM32

2、商家核心服务

STM32STM32STM32

3、商家下单链路服务

STM32STM32

五、探索心得

5.1 总结

借助于精准测试平台,发现一个开发梳理遗漏的接口,有效避免了梳理遗漏导致的测试遗漏,一定程度上规避了风险,是 QA 从经验型的主观判断向精准的数据可视化转变。

借助精准平台识别出一些没有进行自动化覆盖的接口,让 QA 能针对这些清单进行接口自动化的查缺补漏,从另一层面提升了自动化用例集的完整性。

精准平台与自动化平台、测试用例平台、覆盖率平台打通,从正向追溯和逆向追溯两个核心进行测试,确保数据的准确性、完整性,方便 QA 持续跟踪,提高测试效率。

5.2 优化

在接入精准测试平台的过程中,对平台有了进一步的了解,当然在使用的过程中也发现一些问题,并给开发提了相关建议,从而不断完善精准测试平台开发,也帮助 QA 更好更高效得完成质量保障工作。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分