室内有无人检测
·
室内检测有无人系统完整设计方案
1. 系统目标
本终端实现室内有无人检测,基于STM32F103C8T6单片机,并通过 LCD1602 本地显示和串口 JSON 上报完成状态输出。具体功能如下:
- 通过 1 路 PIR 人体红外传感器(LD2402或AM312)检测室内是否有人。
- PIR 检测到人体(高电平)时立即点亮 LED,并在无人持续 5 秒 后熄灭 LED。
- LCD1602 实时显示房间有人/无人状态与 LED 状态。
- USART1(调试)与 USART2(上报)以 JSON 格式同步输出状态数据。
- 业务逻辑相互独立,通过共享全局变量传递状态。
2. 硬件组成与引脚分配
2.1 完整引脚表
根据现有代码,引脚和外设的分配严格遵循下表(PIR 为下拉输入):
| 引脚 | 功能 | 配置方式 | 说明 |
|---|---|---|---|
| PA0 | PIR1 | GPIO Input Pull-down | 人体红外输入,高电平=检测到人 |
| PB8 | LED | GPIO Push-Pull Output | LED 指示灯(高电平亮) |
| PB0 | LCD_D4 | GPIO Push-Pull Output | LCD1602 数据位 4 |
| PB1 | LCD_D5 | GPIO Push-Pull Output | LCD1602 数据位 5 |
| PB10 | LCD_D6 | GPIO Push-Pull Output | LCD1602 数据位 6 |
| PB11 | LCD_D7 | GPIO Push-Pull Output | LCD1602 数据位 7 |
| PB12 | LCD_RS | GPIO Push-Pull Output | LCD1602 寄存器选择 |
| PB13 | LCD_EN | GPIO Push-Pull Output | LCD1602 使能脉冲 |
| PA9 | USART1_TX | 复用推挽输出 | 调试串口(接 Virtual Terminal) |
| PA10 | USART1_RX | 浮空输入 | 调试串口接收 |
| PA2 | USART2_TX | 复用推挽输出 | JSON 状态上报(接 COMPIM) |
| PA3 | USART2_RX | 浮空输入 | 上报接收 |
注:LCD1602 采用 4-bit 并行接口模式。其 RW 引脚直接接地(GND),处于只写模式,不占用 MCU 引脚。
3. STM32CubeMX 配置参数
3.1 时钟配置 (HSI)
- 内置高速时钟 HSI:8 MHz
- 系统主频 (SYSCLK):64 MHz(经 PLL 16倍频)
3.2 外设配置参数
- TIM2:预分频系数(Prescaler)= 6399,自动重装载值(Period)= 9999。开启全局中断(产生 1 Hz 定时节拍)。
- USART1 & USART2:波特率 9600,8位数据位,1位停止位,无校验。
4. 软件架构与库函数实现
4.1 核心 LCD1602 标准显示库
代码集成了自适应 4 位总线的 51 移植版高级显示函数,移除了原方案中未使用的函数(如原方案的 LCD_Print,统一改回代码实际使用的命名):
| 函数原型 | 作用说明 |
|---|---|
void LCD_Init(void); |
初始化 LCD(含 4 位总线软件复位唤醒序列) |
void LCD_WriteCommand(unsigned char Command); |
写入控制命令(0x01清屏时自动追加 3ms 延迟) |
void LCD_WriteData(unsigned char Data); |
写入字符数据 |
void LCD_ShowString(unsigned char Line, unsigned char Column, unsigned char *String); |
在指定行(1-2)和列(1-16)显示字符串 |
4.2 三模块独立运行调度逻辑
在 main.c 的主循环中,系统通过各自的触发机制独立运行,避免互相嵌套阻塞:
- 模块1:PIR 与 LED 控制
- 持续轮询 PA0 的状态。
- 触发有人时,立即点亮 PB8,并清除定时器计数。
- 无人时由 TIM2 中断服务函数
HAL_TIM_PeriodElapsedCallback负责每秒累加,满 5 秒自动关灯。
- 模块2:LCD 本地显示刷新
- 基于软件时间戳节拍,固定每 300 ms 调用一次
LCD_Task_Refresh()刷新屏幕。 - 刷新内容通过长字符串后补空格的方式,自动覆盖旧字符残余,避免频繁调用清屏导致闪烁。
- 基于软件时间戳节拍,固定每 300 ms 调用一次
- 模块3:串口 JSON 数据上报
- 采用状态变化事件触发机制。
- 仅在检测到
presence或led_state发生改变时,触发Send_Status_JSON()发送数据,大大减少串口拥堵。
5. JSON 数据上报格式
当状态改变时,USART1 和 USART2 会同时发送一行合规的标准 JSON 字符串:
JSON
{"id":"T5","presence":0,"led":0}
id:终端唯一标识,固定为"T5"。presence:房间有人状态(1: 有人,0: 无人)。led:当前灯光状态(1: 开启,0: 关闭)。
proteus仿真
硬件接线
面包板效果
main.c代码(STM32CubeMX基于HAL库)
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : 终端5 室内有无人检测系统(4位总线 + 51经典标准显示库移植版)
* 全部业务逻辑与高级显示库函数集成于本文件 USER CODE 区域
*
* 引脚分配:
* PA0 —— PIR1(GPIO 输入,下拉)
* PB8 —— LED(GPIO 推挽输出)
* PB0 / PB1 —— LCD D4 / D5
* PB10 / PB11 —— LCD D6 / D7
* PB12 —— LCD RS
* PB13 —— LCD EN
* PA9 / PA10 —— USART1 TX/RX(调试 Virtual Terminal)
* PA2 / PA3 —— USART2 TX/RX(上报 COMPIM)
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* ── PIR / LED ── */
#define DELAY_OFF_SECONDS 5U /* 无人持续多少秒后关灯 */
/* ── LCD1602 引脚(全部在 GPIOB) ── */
#define LCD_D4_PIN GPIO_PIN_0
#define LCD_D5_PIN GPIO_PIN_1
#define LCD_D6_PIN GPIO_PIN_10
#define LCD_D7_PIN GPIO_PIN_11
#define LCD_RS_PIN GPIO_PIN_12
#define LCD_EN_PIN GPIO_PIN_13
#define LCD_GPIO_PORT GPIOB
/* ── LED 引脚 ── */
#define LED_PIN GPIO_PIN_8
#define LED_GPIO_PORT GPIOB
/* ── PIR 引脚(仅保留PA0下拉) ── */
#define PIR1_PIN GPIO_PIN_0
#define PIR_GPIO_PORT GPIOA
/* ── 刷新节拍 ── */
#define LCD_REFRESH_MS 300U
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim2;
UART_HandleTypeDef huart1;
UART_HandleTypeDef huart2;
/* USER CODE BEGIN PV */
/* ── 系统状态变量 ── */
static volatile uint8_t presence = 0; /* 1=有人 0=无人 */
static volatile uint8_t led_state = 0; /* 1=亮 0=灭 */
static volatile uint32_t no_person_cnt = 0; /* 无人持续秒计数 */
static volatile uint8_t counting_down = 0; /* 是否正在延时关灯 */
/* 记录上一次上报的状态,用于检测变化 */
static uint8_t last_reported_presence = 0;
static uint8_t last_reported_led = 0;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM2_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */
/* ── LCD1602 移植标准库函数声明 ── */
static void Delay_us(uint32_t us);
static void LCD_SendNibble(uint8_t nibble);
static void LCD_SendByte(uint8_t byte, uint8_t rs);
void LCD_Init(void);
void LCD_WriteCommand(unsigned char Command);
void LCD_WriteData(unsigned char Data);
void LCD_SetCursor(unsigned char Line, unsigned char Column);
void LCD_ShowChar(unsigned char Line, unsigned char Column, unsigned char Char);
void LCD_ShowString(unsigned char Line, unsigned char Column, unsigned char *String);
void LCD_ShowNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line, unsigned char Column, int Number, unsigned char Length);
void LCD_ShowHexNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length);
void LCD_ShowBinNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length);
int LCD_Pow(int X, int Y);
/* ── 业务功能函数 ── */
static void LCD_Task_Refresh(void);
static void Send_Status_JSON(void);
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/**
* @brief 微秒级精确延迟(基于当前主频估计,规避 HAL_Delay 毫秒级阻塞)
*/
static void Delay_us(uint32_t us)
{
uint32_t delay = us * 8;
while(delay--)
{
__NOP();
}
}
/* ============================================================
* 1. LCD1602 底层硬件驱动(4位并行模式)
* ============================================================ */
static void LCD_SendNibble(uint8_t nibble)
{
HAL_GPIO_WritePin(LCD_GPIO_PORT, LCD_D4_PIN, (nibble & 0x01) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_GPIO_PORT, LCD_D5_PIN, (nibble & 0x02) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_GPIO_PORT, LCD_D6_PIN, (nibble & 0x04) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_GPIO_PORT, LCD_D7_PIN, (nibble & 0x08) ? GPIO_PIN_SET : GPIO_PIN_RESET);
Delay_us(2); // 地址建立时间
HAL_GPIO_WritePin(LCD_GPIO_PORT, LCD_EN_PIN, GPIO_PIN_SET);
Delay_us(5); // 使能脉冲宽度
HAL_GPIO_WritePin(LCD_GPIO_PORT, LCD_EN_PIN, GPIO_PIN_RESET); // 下降沿锁存
Delay_us(2); // 保持时间
}
static void LCD_SendByte(uint8_t byte, uint8_t rs)
{
HAL_GPIO_WritePin(LCD_GPIO_PORT, LCD_RS_PIN, rs ? GPIO_PIN_SET : GPIO_PIN_RESET);
// 4位总线分高低半字节发送
LCD_SendNibble(byte >> 4);
LCD_SendNibble(byte & 0x0F);
Delay_us(80); // 内部执行处理延迟
}
/* ============================================================
* 2. 移植自 51 参考库的高级显示函数(针对 STM32 4位总线自适应)
* ============================================================ */
void LCD_WriteCommand(unsigned char Command)
{
LCD_SendByte(Command, 0);
if (Command == 0x01)
{
HAL_Delay(3); // 清屏需要额外长延迟
}
}
void LCD_WriteData(unsigned char Data)
{
LCD_SendByte(Data, 1);
}
void LCD_Init(void)
{
HAL_Delay(50); // 上电稳定等待
HAL_GPIO_WritePin(LCD_GPIO_PORT, LCD_RS_PIN, GPIO_PIN_RESET);
// ── 4位并行总线软件复位唤醒序列 ──
LCD_SendNibble(0x03); HAL_Delay(5);
LCD_SendNibble(0x03); HAL_Delay(1);
LCD_SendNibble(0x03); HAL_Delay(1);
LCD_SendNibble(0x02); HAL_Delay(1); // 切换进入 4-bit 模式
// ── 基础参数初始化配置 ──
LCD_WriteCommand(0x28); // 4位总线,两行显示,5x7点阵
LCD_WriteCommand(0x0C); // 显示开,光标关,闪烁关
LCD_WriteCommand(0x06); // 写字符后光标自动加1
LCD_WriteCommand(0x01); // 清屏
}
void LCD_SetCursor(unsigned char Line, unsigned char Column)
{
if(Line == 1)
{
LCD_WriteCommand(0x80 | (Column - 1));
}
else
{
LCD_WriteCommand(0x80 | (Column - 1) + 0x40);
}
}
void LCD_ShowChar(unsigned char Line, unsigned char Column, unsigned char Char)
{
LCD_SetCursor(Line, Column);
LCD_WriteData(Char);
}
void LCD_ShowString(unsigned char Line, unsigned char Column, unsigned char *String)
{
unsigned char i;
LCD_SetCursor(Line, Column);
for(i = 0; String[i] != '\0'; i++)
{
LCD_WriteData(String[i]);
}
}
int LCD_Pow(int X, int Y)
{
unsigned char i;
int Result = 1;
for(i = 0; i < Y; i++)
{
Result *= X;
}
return Result;
}
void LCD_ShowNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line, Column);
for(i = Length; i > 0; i--)
{
LCD_WriteData('0' + Number / LCD_Pow(10, i - 1) % 10);
}
}
void LCD_ShowSignedNum(unsigned char Line, unsigned char Column, int Number, unsigned char Length)
{
unsigned char i;
unsigned int Number1;
LCD_SetCursor(Line, Column);
if(Number >= 0)
{
LCD_WriteData('+');
Number1 = Number;
}
else
{
LCD_WriteData('-');
Number1 = -Number;
}
for(i = Length; i > 0; i--)
{
LCD_WriteData('0' + Number1 / LCD_Pow(10, i - 1) % 10);
}
}
void LCD_ShowHexNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length)
{
unsigned char i;
unsigned char SingleNumber;
LCD_SetCursor(Line, Column);
for(i = Length; i > 0; i--)
{
SingleNumber = Number / LCD_Pow(16, i - 1) % 16;
if(SingleNumber < 10)
{
LCD_WriteData('0' + SingleNumber);
}
else
{
LCD_WriteData('A' + SingleNumber - 10);
}
}
}
void LCD_ShowBinNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line, Column);
for(i = Length; i > 0; i--)
{
LCD_WriteData('0' + Number / LCD_Pow(2, i - 1) % 2);
}
}
/* ============================================================
* 3. 业务刷新与串口上报
* ============================================================ */
static void LCD_Task_Refresh(void)
{
// 利用高层 ShowString 接口,自动覆盖残余字符,更加简洁整齐
if (presence)
LCD_ShowString(1, 1, "Room:Person ");
else
LCD_ShowString(1, 1, "Room:Empty ");
if (led_state)
LCD_ShowString(2, 1, "LED :ON ");
else
LCD_ShowString(2, 1, "LED :OFF ");
}
static void Send_Status_JSON(void)
{
char buf[48];
int len;
len = snprintf(buf, sizeof(buf),
"{\"id\":\"T5\",\"presence\":%d,\"led\":%d}\r\n",
(int)presence,
(int)led_state);
HAL_UART_Transmit(&huart1, (uint8_t *)buf, (uint16_t)len, 100);
HAL_UART_Transmit(&huart2, (uint8_t *)buf, (uint16_t)len, 100);
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM2_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim2);
LCD_Init();
// 使用新标准的库接口显示欢迎界面
LCD_ShowString(1, 1, " Terminal 5 ");
LCD_ShowString(2, 1, " Initializing");
HAL_Delay(1000);
LCD_WriteCommand(0x01); // 清屏
/* 上电发送一次初始状态 */
Send_Status_JSON();
last_reported_presence = presence;
last_reported_led = led_state;
uint32_t lcd_last_tick = HAL_GetTick();
/* USER CODE END 2 */
while (1)
{
/* USER CODE BEGIN 3 */
/* ── 模块1:单引脚 PIR 检测 (PA0 下拉) + LED 控制 ── */
GPIO_PinState pir1 = HAL_GPIO_ReadPin(PIR_GPIO_PORT, PIR1_PIN);
if (pir1 == GPIO_PIN_SET)
{
presence = 1;
led_state = 1;
no_person_cnt = 0;
counting_down = 0;
HAL_GPIO_WritePin(LED_GPIO_PORT, LED_PIN, GPIO_PIN_SET);
}
else
{
presence = 0;
if (led_state == 1 && counting_down == 0)
{
counting_down = 1;
no_person_cnt = 0;
}
}
/* ── 模块2:LCD1602 刷新 ── */
if ((HAL_GetTick() - lcd_last_tick) >= LCD_REFRESH_MS)
{
lcd_last_tick = HAL_GetTick();
LCD_Task_Refresh();
}
/* ── 模块3:USART 上报 ── */
if (presence != last_reported_presence || led_state != last_reported_led)
{
last_reported_presence = presence;
last_reported_led = led_state;
Send_Status_JSON();
}
/* USER CODE END 3 */
}
}
/**
* @brief System Clock Configuration
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL16;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
static void MX_TIM2_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim2.Instance = TIM2;
htim2.Init.Prescaler = 6399;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 9999;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* 配置并开启 TIM2 的 NVIC 中断 */
HAL_NVIC_SetPriority(TIM2_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
}
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 9600;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
static void MX_USART2_UART_Init(void)
{
huart2.Instance = USART2;
huart2.Init.BaudRate = 9600;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
}
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
HAL_GPIO_WritePin(GPIOB,
GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_10 | GPIO_PIN_11 |
GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_8,
GPIO_PIN_RESET);
/* PA0:PIR 输入,配置为下拉模式 */
GPIO_InitStruct.Pin = PIR1_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* PB0/PB1/PB10/PB11(PB12/PB13/PB8) 高频推挽输出 */
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_10 | GPIO_PIN_11 |
GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance != TIM2) return;
if (counting_down)
{
no_person_cnt++;
if (no_person_cnt >= DELAY_OFF_SECONDS)
{
led_state = 0;
counting_down = 0;
no_person_cnt = 0;
HAL_GPIO_WritePin(LED_GPIO_PORT, LED_PIN, GPIO_PIN_RESET);
}
}
}
/* USER CODE END 4 */
void Error_Handler(void)
{
__disable_irq();
while (1) {}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif /* USE_FULL_ASSERT */
``
更多推荐



所有评论(0)