今天从同事那里拿到一个OPT300X(实际是OPT3001)环境光传感器,想让我帮忙测测模块如何驱动,今天就跟大家分享一下这个模块如何玩吧。

拿到这个模块发现这个模块好小啊,从接口丝印这块可以看出采用的是I2C方式进行通讯的,还引出了INT接口,不过我这次没有使用INT中断接口。通过板上的元件丝印可以看出,VCC旁边的SOT23封装是一个PMOS3400,另一个SOT23封装的芯片是622K 5V转3.3V的LDO芯片,4.7K的电阻是I2C的上拉电阻,芯片旁边的电阻是接INT中断引脚的一个上拉电阻。其他几颗是电容,作用是滤波功能。
OPT3001的特点(来源于芯片手册):
该芯片采用精密光学滤波,可与人眼相匹配:可阻隔 99%(典型值)以上的红外线
支持自动满量程设置功能可简化软件并确保进行适当配置
• 测量范围:0.01 Lux 至83,000 Lux
• 23 位有效动态范围,支持自动设置增益范围
• 12 种二进制权满量程范围设置:范围间匹配程度< 0.2%(典型值)
• 低工作电流:1.8µA(典型值)
• 工作温度范围:-40°C 至 +85°C
• 宽电源范围:1.6V 至 3.6V
• 可耐受 5.5V 电压的 I/O
• 灵活的中断系统
• 小封装尺寸:2.0mm x 2.0mm x 0.65mm
芯片手册管脚介绍如下图所示:

分析好模块的电路之后,就可以正常编程了,本次还是采用CH579开发板进行调试,采用模拟I2C的方式进行驱动,#define OPT3001_I2C_ADDR 0x44 // 7位地址,这里需要注意。
OPT3001.c
复制
#include "opt3001.h"#include "CH57x_common.h"// I2C引脚定义//static uint8_t opt3001_sda_pin = GPIO_Pin_7; // 默认引脚//static uint8_t opt3001_scl_pin = GPIO_Pin_6;// 读取SDA引脚状态static uint8_t I2C_SDA_READ(void){// 读取整个端口B,然后检查SDA引脚return (GPIOB_ReadPort() & I2C_SDA_PIN) ? 1 : 0;}// I2C初始化void I2C_Init(void){// 设置引脚为推挽输出,初始状态为高电平GPIOB_ModeCfg(I2C_SDA_PIN | I2C_SCL_PIN, GPIO_ModeOut_PP_5mA);I2C_SDA_HIGH();I2C_SCL_HIGH();}// I2C起始信号void I2C_Start(void){I2C_SDA_OUT();I2C_SDA_HIGH();I2C_SCL_HIGH();DelayUs(5);I2C_SDA_LOW();DelayUs(5);I2C_SCL_LOW();DelayUs(5);}// I2C停止信号void I2C_Stop(void){I2C_SDA_OUT();I2C_SDA_LOW();DelayUs(5);I2C_SCL_HIGH();DelayUs(5);I2C_SDA_HIGH();DelayUs(5);}// I2C发送一个字节uint8_t I2C_SendByte(uint8_t data){uint8_t i, ack;I2C_SDA_OUT();for(i = 0; i < 8; i++){if(data & 0x80)I2C_SDA_HIGH();elseI2C_SDA_LOW();data <<= 1;DelayUs(2);I2C_SCL_HIGH();DelayUs(5);I2C_SCL_LOW();DelayUs(2);}// 读取ACKI2C_SDA_HIGH();I2C_SDA_IN();DelayUs(2);I2C_SCL_HIGH();DelayUs(5);ack = I2C_SDA_READ(); // 返回1表示NACK,0表示ACKI2C_SCL_LOW();I2C_SDA_OUT();return ack; // 返回ACK状态}// I2C接收一个字节uint8_t I2C_ReceiveByte(uint8_t ack){uint8_t i, data = 0;I2C_SDA_HIGH();I2C_SDA_IN();for(i = 0; i < 8; i++){DelayUs(2);I2C_SCL_HIGH();data <<= 1;if(I2C_SDA_READ())data |= 0x01;DelayUs(5);I2C_SCL_LOW();}// 发送ACKI2C_SDA_OUT();if(ack)I2C_SDA_LOW(); // 发送ACKelseI2C_SDA_HIGH(); // 发送NACKDelayUs(2);I2C_SCL_HIGH();DelayUs(5);I2C_SCL_LOW();I2C_SDA_HIGH();return data;}// 初始化OPT3001void OPT3001_Init(void){I2C_Init();DelayMs(10); // 等待传感器稳定}///**// * @brief 初始化OPT3001传感器// * @param sda_pin SDA引脚// * @param scl_pin SCL引脚// *///void OPT3001_Init(uint8_t sda_pin, uint8_t scl_pin)//{// opt3001_sda_pin = sda_pin;// opt3001_scl_pin = scl_pin;//// // 初始化I2C(使用软件I2C)// GPIOB_SetBits(opt3001_sda_pin | opt3001_scl_pin);// GPIOB_ModeCfg(opt3001_sda_pin | opt3001_scl_pin, GPIO_ModeOut_PP_5mA);//}///**// * @brief 读取GPIO引脚状态// * @param pin 引脚编号// * @return 引脚状态:0-低电平,1-高电平// *///static uint8_t GPIO_ReadPin(uint8_t pin)//{// // 方法1:使用GPIOB_ReadPort()函数(无参数)// return (GPIOB_ReadPort() & pin) ? 1 : 0;//// // 方法2:使用GPIO_ReadPortPin()函数(如果有的话)// // return GPIO_ReadPortPin(GPIOB_BASE, pin);//}///**// * @brief I2C起始信号// *///static void I2C_Start(void)//{// GPIOB_SetBits(opt3001_sda_pin);// GPIOB_SetBits(opt3001_scl_pin);// DelayUs(5);// GPIOB_ResetBits(opt3001_sda_pin);// DelayUs(5);// GPIOB_ResetBits(opt3001_scl_pin);//}///**// * @brief I2C停止信号// *///static void I2C_Stop(void)//{// GPIOB_ResetBits(opt3001_sda_pin);// GPIOB_SetBits(opt3001_scl_pin);// DelayUs(5);// GPIOB_SetBits(opt3001_sda_pin);// DelayUs(5);//}///**// * @brief I2C发送一个字节// * @param data 要发送的数据// * @return 0-ACK, 1-NACK// *///static uint8_t I2C_SendByte(uint8_t data)//{// uint8_t i, ack;// uint8_t port_val;// for(i = 0; i < 8; i++)// {// if(data & 0x80)// GPIOB_SetBits(opt3001_sda_pin);// else// GPIOB_ResetBits(opt3001_sda_pin);// data <<= 1;//// DelayUs(2);// GPIOB_SetBits(opt3001_scl_pin);// DelayUs(5);// GPIOB_ResetBits(opt3001_scl_pin);// DelayUs(2);// }//// // 读取ACK - 修正:使用正确的GPIO读取方式// GPIOB_SetBits(opt3001_sda_pin);// GPIOB_ModeCfg(opt3001_sda_pin, GPIO_ModeIN_PU);// DelayUs(2);// GPIOB_SetBits(opt3001_scl_pin);// DelayUs(5);//// // 使用GPIOB_ReadPort()读取整个端口,然后检查指定引脚// port_val = GPIOB_ReadPort(); // 读取端口B的值// ack = (port_val & opt3001_sda_pin) ? 1 : 0;//// GPIOB_ResetBits(opt3001_scl_pin);// GPIOB_ModeCfg(opt3001_sda_pin, GPIO_ModeOut_PP_5mA);//// return ack;//}///**// * @brief I2C接收一个字节// * @param ack 是否发送ACK// * @return 接收到的数据// *///static uint8_t I2C_ReceiveByte(uint8_t ack)//{// uint8_t i, data = 0;// uint8_t pin_state;//// GPIOB_SetBits(opt3001_sda_pin);// GPIOB_ModeCfg(opt3001_sda_pin, GPIO_ModeIN_PU);//// for(i = 0; i < 8; i++)// {// DelayUs(2);// GPIOB_SetBits(opt3001_scl_pin);// data <<= 1;//// // 读取SDA引脚状态// pin_state = GPIOB_ReadPort(); // 读取整个端口// if(pin_state & opt3001_sda_pin)// data |= 0x01;//// DelayUs(5);// GPIOB_ResetBits(opt3001_scl_pin);// }//// // 发送ACK// GPIOB_ModeCfg(opt3001_sda_pin, GPIO_ModeOut_PP_5mA);// if(ack)// GPIOB_ResetBits(opt3001_sda_pin);// else// GPIOB_SetBits(opt3001_sda_pin);//// DelayUs(2);// GPIOB_SetBits(opt3001_scl_pin);// DelayUs(5);// GPIOB_ResetBits(opt3001_scl_pin);//// return data;//}/*** @brief 写入OPT3001寄存器* @param reg 寄存器地址* @param data 要写入的数据* @return OPT3001_Status_t 状态*/static OPT3001_Status_t OPT3001_WriteRegister(uint8_t reg, uint16_t data){uint8_t i;uint8_t buffer[3];buffer[0] = reg;buffer[1] = (data >> 8) & 0xFF; // 高位在前buffer[2] = data & 0xFF;I2C_Start();// 发送设备地址(写)if(I2C_SendByte(OPT3001_I2C_ADDR << 1)){I2C_Stop();return OPT3001_ERROR_I2C;}// 发送寄存器地址和数据for(i = 0; i < 3; i++){if(I2C_SendByte(buffer[i])){I2C_Stop();return OPT3001_ERROR_I2C;}}I2C_Stop();return OPT3001_OK;}/*** @brief 读取OPT3001寄存器* @param reg 寄存器地址* @param data 读取的数据* @return OPT3001_Status_t 状态*/static OPT3001_Status_t OPT3001_ReadRegister(uint8_t reg, uint16_t *data){uint8_t msb,lsb;I2C_Start();// 发送设备地址(写)if(I2C_SendByte(OPT3001_I2C_ADDR << 1)){I2C_Stop();return OPT3001_ERROR_I2C;}// 发送寄存器地址if(I2C_SendByte(reg)){I2C_Stop();return OPT3001_ERROR_I2C;}// 重新启动I2C_Start();// 发送设备地址(读)if(I2C_SendByte((OPT3001_I2C_ADDR << 1) | 0x01)){I2C_Stop();return OPT3001_ERROR_I2C;}// 读取数据(高位在前)msb = I2C_ReceiveByte(1); // 发送ACKlsb = I2C_ReceiveByte(0); // 发送NACKI2C_Stop();*data = (msb << 8) | lsb;return OPT3001_OK;}/*** @brief 读取制造商ID* @param id 制造商ID* @return OPT3001_Status_t 状态*/OPT3001_Status_t OPT3001_ReadManufacturerID(uint16_t *id){return OPT3001_ReadRegister(OPT3001_REG_MANUFACTURER, id);}/*** @brief 读取设备ID* @param id 设备ID* @return OPT3001_Status_t 状态*/OPT3001_Status_t OPT3001_ReadDeviceID(uint16_t *id){return OPT3001_ReadRegister(OPT3001_REG_DEVICE_ID, id);}/*** @brief 配置OPT3001* @param config 配置值* @return OPT3001_Status_t 状态*/OPT3001_Status_t OPT3001_Configure(uint16_t config){return OPT3001_WriteRegister(OPT3001_REG_CONFIG, config);}/*** @brief 开始单次转换* @return OPT3001_Status_t 状态*/OPT3001_Status_t OPT3001_StartSingleConversion(void){uint16_t config;OPT3001_Status_t status;// 读取当前配置status = OPT3001_ReadRegister(OPT3001_REG_CONFIG, &config);if(status != OPT3001_OK)return status;// 设置为单次转换模式config &= ~OPT3001_CONFIG_MODE_MASK;config |= OPT3001_CONFIG_MODE_SINGLE;return OPT3001_WriteRegister(OPT3001_REG_CONFIG, config);}/*** @brief 读取光照强度(lux)* @param lux 光照强度值* @return OPT3001_Status_t 状态*/OPT3001_Status_t OPT3001_ReadLux(float *lux){uint16_t raw_data,exponent,mantissa;OPT3001_Status_t status;status = OPT3001_ReadRawData(&raw_data);if(status != OPT3001_OK)return status;// 检查溢出标志if(raw_data & 0x8000){return OPT3001_ERROR_OVERFLOW;}// 提取指数和尾数exponent = (raw_data >> 12) & 0x0F;mantissa = raw_data & 0x0FFF;// 计算lux值:lux = 0.01 * 2^exponent * mantissa*lux = 0.01 * (1 << exponent) * mantissa;return OPT3001_OK;}/*** @brief 读取原始数据* @param raw 原始数据* @return OPT3001_Status_t 状态*/OPT3001_Status_t OPT3001_ReadRawData(uint16_t *raw){return OPT3001_ReadRegister(OPT3001_REG_RESULT, raw);}/*** @brief 设置高限阈值* @param lux 阈值(lux)* @return OPT3001_Status_t 状态*/OPT3001_Status_t OPT3001_SetHighLimit(float lux){uint16_t mantissa;uint8_t exponent = 0;// 将lux转换为寄存器值uint16_t reg_value = 0;if(lux > 838656) lux = 838656; // 最大值// 找到合适的量程while(lux > 4095 * 0.01 * (1 << exponent) && exponent < 11){exponent++;}if(exponent > 11) exponent = 11;// 计算尾数mantissa = (uint16_t)(lux / (0.01 * (1 << exponent)));if(mantissa > 4095) mantissa = 4095;reg_value = (exponent << 12) | mantissa;return OPT3001_WriteRegister(OPT3001_REG_HIGH_LIMIT, reg_value);}/*** @brief 设置低限阈值* @param lux 阈值(lux)* @return OPT3001_Status_t 状态*/OPT3001_Status_t OPT3001_SetLowLimit(float lux){uint16_t mantissa;uint8_t exponent = 0;// 实现同SetHighLimituint16_t reg_value = 0;if(lux > 838656) lux = 838656;while(lux > 4095 * 0.01 * (1 << exponent) && exponent < 11){exponent++;}if(exponent > 11) exponent = 11;mantissa = (uint16_t)(lux / (0.01 * (1 << exponent)));if(mantissa > 4095) mantissa = 4095;reg_value = (exponent << 12) | mantissa;return OPT3001_WriteRegister(OPT3001_REG_LOW_LIMIT, reg_value);}/*** @brief 等待转换完成* @return OPT3001_Status_t 状态*/OPT3001_Status_t OPT3001_WaitForConversion(void){uint16_t config;uint32_t timeout = 1000; // 超时时间do {DelayMs(1);if(OPT3001_ReadRegister(OPT3001_REG_CONFIG, &config) != OPT3001_OK)return OPT3001_ERROR_I2C;if(timeout-- == 0)return OPT3001_ERROR_TIMEOUT;} while(!(config & OPT3001_CONFIG_CRF)); // 等待转换完成标志return OPT3001_OK;}
OPT3001.h
复制
#ifndef __OPT3001_H__#define __OPT3001_H__#include "CH57x_common.h"// 软件I2C引脚定义#define I2C_SDA_PIN GPIO_Pin_7#define I2C_SCL_PIN GPIO_Pin_6// 引脚操作宏#define I2C_SDA_HIGH() GPIOB_SetBits(I2C_SDA_PIN)#define I2C_SDA_LOW() GPIOB_ResetBits(I2C_SDA_PIN)#define I2C_SCL_HIGH() GPIOB_SetBits(I2C_SCL_PIN)#define I2C_SCL_LOW() GPIOB_ResetBits(I2C_SCL_PIN)#define I2C_SDA_IN() GPIOB_ModeCfg(I2C_SDA_PIN, GPIO_ModeIN_PU)#define I2C_SDA_OUT() GPIOB_ModeCfg(I2C_SDA_PIN, GPIO_ModeOut_PP_5mA)// OPT3001 I2C地址#define OPT3001_I2C_ADDR 0x44 // 7位地址// 寄存器地址#define OPT3001_REG_RESULT 0x00#define OPT3001_REG_CONFIG 0x01#define OPT3001_REG_LOW_LIMIT 0x02#define OPT3001_REG_HIGH_LIMIT 0x03#define OPT3001_REG_MANUFACTURER 0x7E#define OPT3001_REG_DEVICE_ID 0x7F// 配置寄存器位定义#define OPT3001_CONFIG_RN_MASK 0xF000#define OPT3001_CONFIG_RN_AUTO 0xC000 // 自动量程#define OPT3001_CONFIG_CT 0x0800 // 转换时间:0=100ms,1=800ms#define OPT3001_CONFIG_MODE_MASK 0x0600#define OPT3001_CONFIG_MODE_SHUTDOWN 0x0000 // 关机模式#define OPT3001_CONFIG_MODE_SINGLE 0x0200 // 单次转换#define OPT3001_CONFIG_MODE_CONTINUOUS 0x0600 // 连续转换#define OPT3001_CONFIG_OVF 0x0100 // 溢出标志#define OPT3001_CONFIG_CRF 0x0080 // 转换就绪标志#define OPT3001_CONFIG_FH 0x0040 // 超出高限标志#define OPT3001_CONFIG_FL 0x0020 // 低于低限标志#define OPT3001_CONFIG_L 0x0010 // 锁存器#define OPT3001_CONFIG_POL 0x0008 // 极性#define OPT3001_CONFIG_ME 0x0004 // 屏蔽使能#define OPT3001_CONFIG_FC_MASK 0x0003 // 故障计数// 量程和分辨率typedef enum {OPT3001_RANGE_AUTO = 0xC000,OPT3001_RANGE_40_95 = 0x0000,OPT3001_RANGE_0_156 = 0x1000,OPT3001_RANGE_0_625 = 0x2000,OPT3001_RANGE_1_25 = 0x3000,OPT3001_RANGE_2_5 = 0x4000,OPT3001_RANGE_5 = 0x5000,OPT3001_RANGE_10 = 0x6000,OPT3001_RANGE_20 = 0x7000,OPT3001_RANGE_40 = 0x8000,OPT3001_RANGE_80 = 0x9000} OPT3001_Range_t;// 转换模式typedef enum {OPT3001_MODE_SHUTDOWN = 0,OPT3001_MODE_SINGLE,OPT3001_MODE_CONTINUOUS} OPT3001_Mode_t;// 错误代码typedef enum {OPT3001_OK = 0,OPT3001_ERROR_I2C,OPT3001_ERROR_TIMEOUT,OPT3001_ERROR_OVERFLOW,OPT3001_ERROR_NOT_READY} OPT3001_Status_t;// 函数声明//void OPT3001_Init(uint8_t sda_pin, uint8_t scl_pin);void OPT3001_Init(void);static uint8_t GPIO_ReadPin(uint8_t pin);OPT3001_Status_t OPT3001_ReadManufacturerID(uint16_t *id);OPT3001_Status_t OPT3001_ReadDeviceID(uint16_t *id);OPT3001_Status_t OPT3001_Configure(uint16_t config);OPT3001_Status_t OPT3001_StartSingleConversion(void);OPT3001_Status_t OPT3001_ReadLux(float *lux);OPT3001_Status_t OPT3001_ReadRawData(uint16_t *raw);OPT3001_Status_t OPT3001_SetHighLimit(float lux);OPT3001_Status_t OPT3001_SetLowLimit(float lux);OPT3001_Status_t OPT3001_WaitForConversion(void);#endif /* __OPT3001_H__ */
main.c
复制
/********************************** (C) COPYRIGHT ******************************** File Name : Main.c* Author : WCH* Version : V1.0* Date : 2018/12/15* Description : 串口1收发演示********************************************************************************** Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.* Attention: This software (modified or not) and binary are used for* microcontroller manufactured by Nanjing Qinheng Microelectronics.*******************************************************************************/#include "CH57x_common.h"UINT8 TxBuff[]="This is a tx exam\r\n";UINT8 RxBuff[100];UINT8 trigB;int main(){float lux;uint16_t manufacturer_id, device_id;OPT3001_Status_t status;uint16_t config;/* 配置串口1:先配置IO口模式,再配置串口 */GPIOA_SetBits(GPIO_Pin_9);GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU); // RXD-配置上拉输入GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA); // TXD-配置推挽输出,注意先让IO口输出高电平UART1_DefInit();// 初始化OPT3001(使用PB6-SCL, PB7-SDA)OPT3001_Init();// 读取制造商ID和设备IDstatus = OPT3001_ReadManufacturerID(&manufacturer_id);if(status == OPT3001_OK){printf("Manufacturer ID: 0x%04X\r\n", manufacturer_id);}status = OPT3001_ReadDeviceID(&device_id);if(status == OPT3001_OK){printf("Device ID: 0x%04X\r\n", device_id);}// 配置OPT3001:自动量程,单次转换模式,转换时间800msconfig = OPT3001_CONFIG_RN_AUTO | // 自动量程OPT3001_CONFIG_CT | // 800ms转换时间OPT3001_CONFIG_MODE_SINGLE; // 单次转换模式status = OPT3001_Configure(config);if(status != OPT3001_OK){printf("Configuration failed: %d\r\n", status);}// 设置阈值OPT3001_SetHighLimit(10000.0); // 高限1000 luxOPT3001_SetLowLimit(1.0); // 低限10 luxwhile(1){// 开始单次转换status = OPT3001_StartSingleConversion();if(status != OPT3001_OK){printf("Start conversion failed: %d\r\n", status);DelayMs(1000);continue;}// 等待转换完成status = OPT3001_WaitForConversion();if(status != OPT3001_OK){printf("Wait conversion failed: %d\r\n", status);DelayMs(1000);continue;}// 读取光照强度status = OPT3001_ReadLux(&lux);if(status == OPT3001_OK){printf("Light: %.2f lux\r\n", lux);}else if(status == OPT3001_ERROR_OVERFLOW){printf("Light overflow!\r\n");}else{printf("Read error: %d\r\n", status);}DelayMs(2000); // 每2秒读取一次}#if 0 // 测试串口发送字符串UART1_SendString( TxBuff, sizeof(TxBuff) );#endif#if 0 // 查询方式:接收数据后发送出去while(1){len = UART1_RecvString(RxBuff);if( len ){UART1_SendString( RxBuff, len );}}#endif#if 0 // 中断方式:接收数据后发送出去UART1_ByteTrigCfg( UART_7BYTE_TRIG );trigB = 7;UART1_INTCfg( ENABLE, RB_IER_RECV_RDY|RB_IER_LINE_STAT );NVIC_EnableIRQ( UART1_IRQn );#endifwhile(1);}void UART1_IRQHandler(void){UINT8 i;switch( UART1_GetITFlag() ){case UART_II_LINE_STAT: // 线路状态错误UART1_GetLinSTA();break;case UART_II_RECV_RDY: // 数据达到设置触发点for(i=0; i!=trigB; i++){RxBuff[i] = UART1_RecvByte();UART1_SendByte(RxBuff[i]);}break;case UART_II_RECV_TOUT: // 接收超时,暂时一帧数据接收完成i = UART1_RecvString(RxBuff);UART1_SendString( RxBuff, i );break;case UART_II_THR_EMPTY: // 发送缓存区空,可继续发送break;case UART_II_MODEM_CHG: // 只支持串口0break;default:break;}}
需要注意的是,拿到这个模块之后,由于没有关于ADDR的地址设置,因此我就拿万用表测量了芯片第二引脚,发现该引脚电压在1.5V左右,我以为是悬空就默认弱上拉,因此就没处理该引脚,发现调试的时候根本没有反应,最后把该引脚接地之后发现才可以。
最后仿真发现可以正确的读取芯片的ID了。

如果被遮挡,该值会变得很小,如果有强光,则该值会变得很大。如果想要测量的lux范围变得大一些,则可以设置阈值。
// 设置阈值
OPT3001_SetHighLimit(10000.0); //高限10000 lux
OPT3001_SetLowLimit(1.0); // 低限1 lux
总结:
1、 买模块一定要研究原理图,同时关于I2C的话一定要注意ADDR,关于OPT3001的芯片,ADDR接地则需要地址设置成0x44,如果接VDD的话需要地址设置成0x45,一般默认接地。
2、 关于I2C的话,建议是采用硬件的I2C,不过用模拟I2C的话可以很方便的移植,就是复杂在于时序的控制,如果用逻辑分析仪则可以很容易调试。
最后,基于OPT3001的驱动就调试好啦,发现还是比较容易调试的,主要在于其I2C接口比较容易对接,同时芯片资料很多才造就了这么顺利的调试。最后祝大家调试都可以很顺利,新年快乐~



。
---------------------
作者:yangjiaxu
链接:https://bbs.21ic.com/icview-3505512-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

342

被折叠的 条评论
为什么被折叠?



