再出发,从点灯开始

如果你是来看情感片的,那么,恭喜你 ~~~~ 你现在已经看完了,可以走了~~~~

这是一篇准跑题文,所以整个副标题吧:

"多年后再用STM32点亮LED"

先贴上文章总览:

  1. 故事起源(为什么要做这个);
  2. 故事发展(分析如何实现);
  3. 故事高潮(开始Ctrl-C/Ctrl-V);
  4. 故事结尾(下载调试);
  5. 升华主题(接下来要干啥);

开始发车了,,,小司机老司机们要坐稳了。


第一章:故事的起源:

真的没有故事,还不死心想看故事的人,你们可以return;了。故事是这样的。。。

之前因某某(暂时保密)原因做了个电路,电路中MCU控制LED进行亮和灭。感觉我是描述不清了,直接看图吧,如下:


5319976-d1823f97d62956f2.png
就是这个电路了

细心的人一眼就看出了这个MCU是STM32,对的,这里用的其实是STM32F103Cx。MCU和外设电路都是采用同一个3.3V供电。可以看到两颗LED和MCU的PA9管脚的连接网络是STA_LED信号。本来的需求很简单,就是LED_B亮的时候LED_R灭,LED_B灭的时候LED_R亮(以后就叫LED_B是蓝灯,LED_R是红灯)。

这样很好实现啊,那我控制PA9输出高电平就可以蓝灯灭红灯亮了。再控制PA9输出低电平就可以蓝灯亮红灯灭了。

以为事情就这样简单的结束了?不能这么天真。所以接下来开始作了。。。

新需求:
1. 在某种运行状态下蓝色LED单独闪烁,此时红色LED处于熄灭状态;
2. 在某种运行状态下红色LED单独闪烁,此时蓝色LED处于熄灭状态;
3. 在某种运行状态下蓝色LED是呼吸灯,此时红色LED处于熄灭状态且CPU可执行其他任务;

啥?在说啥?第一反应是这个需求实现不了啊,很明显两个LED处于互斥状态,其中一个亮另外一个必定是灭的啊~ 先别急往下看,分析一下能实现这个三个需求吗?要是能想到怎么办就可以return;了。也没必要往下浪费时间了。

第二章 故事发展

不知道你们是怎么想的,反正我一开始是拒绝的,直到再看了一眼电路。。。 从此心里就有了你(好像画风有点不对)

可以看到LED(0603封装)电路的连接情况是: 3.3V --> D1(蓝) --> R10(1.5K) --> PA9 --> D2(红) --> R12(1.5K) --> GND。
有什么用呢?我们先不管PA9,此时电路的连接是:3.3V --> D1(蓝) --> R10(1.5K) --> D2(红) --> R12(1.5K) --> GND。这个电路会亮吗?会不会亮需要数据来推测:


5319976-cb2ca4f227ee5e73.png
这是在网上随便查的一张表

咦~ 原来蓝色这么大啊。就是说红色 + 蓝色 范围是4.92 ~ 5.18V,所以3.3V < 4.92V不能同时点亮这两个LED喽,是这样算的吗?真的不太懂,有专业的可以来给计算一下。不会算怎么办,那就直接动手测试吧,找了一个红色LED(0603)测试发现1.6V就可以点亮了,只是非常的微弱几乎看不到。到这里就基本就证实3.3V无法同时点亮这两个LED了,额,,,还不明白的自己想办法明白去吧。

不过上面的分析到底是要干嘛?你想想,如果控制两颗同时熄灭那只要PA9处于floating状态,或者高阻状态就好了吗?所以需求1在理论上已经解决了。啥?还不明白?细说一下吧,当想让蓝色LED亮的时候设置PA9为低并且推挽输出,当想让蓝色LED灭的时候设置PA9为开漏输出并且为高(此时PA9处于高阻态),整个过程中PA9一直没有高电平,所以红色LED一直处于熄灭状态。

有了需求1 的解决思路那就可以尝试用这个思路去解决需求2了。“需求2. 在某种运行状态下红色LED单独闪烁,此时蓝色LED处于熄灭状态;” 我们这样做:当点亮红色LED的时候设置PA9为高并且推挽输出,当熄灭红色LED的时候设置PA9为高并且开漏输出(此时PA9处于高阻态),整个过程中PA9一直没有低电平,所以蓝色LED一直处于熄灭状态。

现在解决了需求1和需求2接下来可以分析需求3了。需求3:“在某种运行状态下蓝色LED是呼吸灯,此时红色LED处于熄灭状态且CPU可执行其他任务;”。最后一点CPU可执行其他任务就是说需求3的呼吸灯执行过程中CPU基本不参与呗,那只能通过CPU的外设来实现了,毫无疑问就是用定时器和中断处理了。用哪个定时器呢?我们看到蓝色LED是连接在PA9上的,查一下STM32的数据手册吧。

5319976-59406042c3cf7ac2.png
定时器1

我查了半天发现PA9可以作为TIM1的CH2通道输出,输出啥呢?要想实现呼吸灯那就用PWM吧,所以就用TIM1的CH2通道在PA9输出PWM波吧,但是怎么调节亮度呢?可以选择调节PWM频率也可以选择调节占空比,我们就选择经典的调节占空比的方式吧。多久调节一次?我们知道人眼能分辨的频率一般在24-30帧(是这样吗?),所以我们的调节时间间隔应该不大于33mS,否则就能看到明显的亮度阶梯。那就10mS调节一次好了。谁去计时10mS?CPU?它还有其他的活干呢,所以再启动一个定时器TIM3去计时吧。TIM3每计时10mS更新一次TIM1的PWM的占空比就好了。最外头的PA9要怎么设置?既然全程需要红色LED熄灭,那就只能开漏了,又要输出PWM,所以PA9要设置成复用开漏输出。


5319976-d0a06324b680d8f7.png
端口配置寄存器

至此,分析完毕,接下来就是干货时间了。

第三章 故事高潮

开始写代码了,理论分析已经完成,接下来就是把理论变成代码的时候了。
怎么写?不会写怎么办?Ctrl-C / Ctrl-V 总该会吧。

// 0.包含头文件
#include "timer.h"
#include "delay.h"
#include "sys.h"

/***************************************************************************/
// 1. 首先我们定义一下PA9管脚:我们将PA9这个连接到LED的端口定义为DBG_LED
#define DBG_LED_RCC     RCC_APB2Periph_GPIOA
#define DBG_LED_GPIO    GPIOA
#define DBG_LED_PIN     GPIO_Pin_9
#define DBG_LED_INDEX   9
#define DEG_LED_MODE    GPIO_Mode_Out_OD
#define DBG_LED_SPEED   GPIO_Speed_50MHz

// 2. 通过以上分析我们知道总共有以下这么几种状态模式:
typedef enum
{ 
    All_Off = 0,  //全部熄灭模式
    General,      //普通模式,就是红灯亮绿灯灭、绿灯亮红灯灭的互斥状态
    Single_Blue,  //绿灯单独闪烁模式
    Single_Red,   //红灯单独闪烁的模式
    Pwm_Blue      //绿灯呼吸的模式
}LedStatus;
volatile LedStatus currentState = All_Off;    //定义一个全局的用来表示DBG_LED的当前状态的变量

// 3. 每种状态时PA9输出寄存器的值设置
typedef enum
{
    OFF = 0,
    ON
}LedValue;
volatile LedValue currentLedValue = OFF;  //定义一个全局变量用来存储当前LDBG_LED输出寄存器的值

// 4. 初始化DBG_LED 管脚
void LED_Init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(DBG_LED_RCC , ENABLE);  
    
    GPIO_InitStructure.GPIO_Pin = DBG_LED_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //开漏
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_ResetBits(DBG_LED_GPIO, DBG_LED_PIN);  
    GPIO_Init(DBG_LED_GPIO, &GPIO_InitStructure);
}

// 5. 输出状态寄存器(GPIOx->BRR 、 GPIOx->BSRR)配置值
void LED_ConfigValue(GPIO_TypeDef* GPIOx, uint8_t GPIO_Pin, LedValue nextValue)
{
    uint8_t posbit = 0;
    if(currentLedValue == nextValue) return;
    if((currentState == All_Off) || (currentState == Pwm_Blue)) return;
    switch(currentState)
    {
        case General:
        {
            if(nextValue == ON) GPIOx->BSRR = 0x01 << GPIO_Pin;
            else GPIOx->BRR = 0x01 << GPIO_Pin;
        }
        break;
        case Single_Blue:
        {            
            if(nextValue == ON) GPIOx->BRR = 0x1 << GPIO_Pin;
            else GPIOx->BSRR = 0x1 << GPIO_Pin;
        }
        break;
        case Single_Red:
        {
            posbit = (GPIO_Pin >= 8) ? (4 * (GPIO_Pin - 8)) : (4 * GPIO_Pin);
            if(nextValue == ON ) {
                if(GPIO_Pin >= 8) GPIOx->CRH &= ~(0xC << posbit);
                else GPIOx->CRL &= ~(0xC << posbit);
            }
            else {
                if(GPIO_Pin >= 8) GPIOx->CRH |= (0x4 << posbit); 
                else GPIOx->CRL |= (0x4 << posbit); 
            }
        }
        break;
        default: break;
    }
    currentLedValue = nextValue;
}

// 6. 各种状态模式切换处理函数
void LED_State_Handler(GPIO_TypeDef* GPIOx, uint8_t GPIO_Pin, LedStatus nextState)
{
    uint8_t posbit = 0;
    if(currentState == nextState) return;
    posbit = (GPIO_Pin >= 8) ? (4 * (GPIO_Pin - 8)) : (4 * GPIO_Pin);
    
    switch(nextState)
    {
        case All_Off: {
            if(currentState == Pwm_Blue) {
                PWM_TIM_OFF();
            }
            GPIOx->BSRR = 0x1 << GPIO_Pin; 
            if(GPIO_Pin >= 8) {
                GPIOx->CRH &= ~(0xC << posbit); 
                GPIOx->CRH |= 0x4 << posbit;            
            } else {
                GPIOx->CRL &= ~(0xC << posbit); 
                GPIOx->CRL |= 0x4 << posbit;                
            }
        } 
        break;
        case General: {
            if(currentState == Pwm_Blue) {
                PWM_TIM_OFF();
            }
            if(GPIO_Pin >= 8) {
                GPIOx->CRH &= ~(0xC << posbit);
            } else {
                GPIOx->CRL &= ~(0xC << posbit);
            }
            GPIOx->BSRR = 0x1 << GPIO_Pin;
        } 
        break;
        case Single_Blue: {
            if(currentState == Pwm_Blue) {
                PWM_TIM_OFF();
            }
            GPIOx->BSRR = 0x1 << GPIO_Pin; 
            if(GPIO_Pin >= 8) {
                GPIOx->CRH &= ~(0xC << posbit);            
                GPIOx->CRH |= (0x4 << posbit);            
            } else {
                GPIOx->CRL &= ~(0xC << posbit);            
                GPIOx->CRL |= (0x4 << posbit);           
            }
        } 
        break;
        case Single_Red: {
            if(currentState == Pwm_Blue) {
                PWM_TIM_OFF();
            }
            GPIOx->BSRR = 0x1 << GPIO_Pin;  
            if(GPIO_Pin >= 8) {
                GPIOx->CRH &= ~(0xC << posbit);
                GPIOx->CRH |= (0x4 << posbit);          
            } else {
                GPIOx->CRL &= ~(0xC << posbit);
                GPIOx->CRL |= (0x4 << posbit);
            }
        } 
        break;
        case Pwm_Blue: {
            PWM_TIM_ON();
            GPIOx->BRR = 0x1 << GPIO_Pin;  
            if(GPIO_Pin >= 8) {
                GPIOx->CRH &= ~(0xC << posbit); 
                GPIOx->CRH |= 0xC << posbit;           
            } else {
                GPIOx->CRL &= ~(0xC << posbit); 
                GPIOx->CRL |= 0xC << posbit;
            }
        } 
        break;
        default: break;
    }
    currentState = nextState;
}

// 7. 获取引脚编号算法,如传入GPIO_Pin_9 返回9,这里没有使用这个算法。
uint8_t GetPinPos(uint16_t PINx)
{
    uint8_t pos = 0;
    assert_param(IS_GET_GPIO_PIN(PINx));
    if(PINx >= GPIO_Pin_8)
    {
        while((0x01<<(pos + 8)) != PINx) pos++;
        return pos + 8;  
    }
    else
    {
        while((0x01<<pos) != PINx) pos++;
        return pos; 
    }
}

// 8. 主函数
int main(void)
{
    uint8_t cnt = 0;
    delay_init(72);        //延时初始化
    NVIC_Configuration();  //设置中断分组
    LED_Init();            //DBG_LED初始化
    TIM1_PWM_Init(99, 71); //PWM = 72000000/(99+1)/(71+1)=10Khz
    TIM3_Int_Init(9999,71);//T = 10ms not enable
    
    //************第一个测试:普通模式,红绿LED交替闪烁****************************
    LED_State_Handler(DBG_LED_GPIO, DBG_LED_INDEX, General);//配置成普通模式,交替闪烁5次
    for(cnt = 0; cnt < 5; cnt++)
    {
        LED_ConfigValue(DBG_LED_GPIO, DBG_LED_INDEX, ON);
        delay_ms(500);
        LED_ConfigValue(DBG_LED_GPIO, DBG_LED_INDEX, OFF);
        delay_ms(500);
    }
    LED_State_Handler(DBG_LED_GPIO, DBG_LED_INDEX, All_Off);//切换到全部熄灭模式
    delay_ms(1000);
    
    //************第二个测试:绿灯单独闪烁模式************************************
    LED_State_Handler(DBG_LED_GPIO, DBG_LED_INDEX, Single_Blue);//配置成绿灯单独闪烁模式,闪烁5次
    for(cnt = 0; cnt < 5; cnt++)
    {
        LED_ConfigValue(DBG_LED_GPIO, DBG_LED_INDEX, ON);
        delay_ms(500);
        LED_ConfigValue(DBG_LED_GPIO, DBG_LED_INDEX, OFF);
        delay_ms(500);
    }
    LED_State_Handler(DBG_LED_GPIO, DBG_LED_INDEX, All_Off);
    delay_ms(1000);

    //************第三个测试:红灯单独闪烁模式************************************
    LED_State_Handler(DBG_LED_GPIO, DBG_LED_INDEX, Single_Red);//配置成红灯单独闪烁模式,闪烁5次
    for(cnt = 0; cnt < 5; cnt++)
    {
        LED_ConfigValue(DBG_LED_GPIO, DBG_LED_INDEX, ON);
        delay_ms(500);
        LED_ConfigValue(DBG_LED_GPIO, DBG_LED_INDEX, OFF);
        delay_ms(500);
    }
    LED_State_Handler(DBG_LED_GPIO, DBG_LED_INDEX, All_Off);
    delay_ms(1000);
    
    //************第四个测试:绿灯呼吸模式******************************************
    LED_State_Handler(DBG_LED_GPIO, DBG_LED_INDEX, Pwm_Blue);//配置成绿灯呼吸模式,执行5s
    for(cnt = 0; cnt < 5; cnt++)
    {
        delay_ms(1000);
    }
    LED_State_Handler(DBG_LED_GPIO, DBG_LED_INDEX, All_Off);
    delay_ms(1000);
    
    //************第五个测试:绿灯呼吸模式,CPU去执行其他,这里CPU去执行空操****************
    LED_State_Handler(DBG_LED_GPIO, DBG_LED_INDEX, Pwm_Blue);
    while(1)
    {
        ;
    }
}

接下来就是timer.c文件的代码了,不要怪我没有注释,Keil中的注释在这里编码就乱了,所以被我删了。

#include "timer.h"
//********************************************************************************
void TIM3_Int_Init(u16 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); 

    TIM_TimeBaseStructure.TIM_Period = arr; 
    TIM_TimeBaseStructure.TIM_Prescaler =psc; 
    TIM_TimeBaseStructure.TIM_ClockDivision = 0; 
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
 
    TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); 

    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; 
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
    NVIC_Init(&NVIC_InitStructure);  

    //TIM_Cmd(TIM3, ENABLE);                     
}

uint8_t cnt10ms = 8, flag = 1, cnt = 0, ledPwmOpen = 0;

void TIM3_IRQHandler(void)  
{
    if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) 
    {
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update  ); 
        flag ? cnt++ : cnt--;
        if(cnt >= 99) flag = 0;
        if(cnt == 0)  flag = 1; 
        TIM_SetCompare2(TIM1, cnt); 
    }
}

void TIM3_PWM_Init(u16 arr,u16 psc)
{  
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_OCInitTypeDef  TIM_OCInitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE);  
    
    //A6 A7 B0 B1
    GPIO_InitStructure.GPIO_Pin = 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_0 | GPIO_Pin_1; 
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);//³õʼ»¯GPIO
    
    //B4 B5 B0 B1
//  GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); 
    
//  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_0 | GPIO_Pin_1; 
//  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  
//  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//  GPIO_Init(GPIOB, &GPIO_InitStructure);

//  GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE);
   
    TIM_TimeBaseStructure.TIM_Period = arr; 
    TIM_TimeBaseStructure.TIM_Prescaler =psc; 
    TIM_TimeBaseStructure.TIM_ClockDivision = 0; 
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); 
     
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; 
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; 
    TIM_OC1Init(TIM3, &TIM_OCInitStructure);  
    TIM_OC2Init(TIM3, &TIM_OCInitStructure);  
    TIM_OC3Init(TIM3, &TIM_OCInitStructure);  
    TIM_OC4Init(TIM3, &TIM_OCInitStructure);  
    
    TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);  
    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  
    TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  
    TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);  
 
    TIM_Cmd(TIM3, ENABLE); 
}

void TIM1_PWM_Init(u16 arr,u16 psc)
{  
    //GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_OCInitTypeDef  TIM_OCInitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);  

//  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; 
//  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; 
//  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//  GPIO_Init(GPIOA, &GPIO_InitStructure);//³õʼ»¯GPIO
//  GPIO_ResetBits(GPIOA,GPIO_Pin_9);   

    TIM_TimeBaseStructure.TIM_Period = arr; 
    TIM_TimeBaseStructure.TIM_Prescaler =psc; 
    TIM_TimeBaseStructure.TIM_ClockDivision = 0; 
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
     
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; 
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; 
    //TIM_OCInitStructure.TIM_Pulse = 5000;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
    TIM_OC2Init(TIM1, &TIM_OCInitStructure);  
    TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable); 
 
    TIM_Cmd(TIM1, ENABLE);  
    TIM_CtrlPWMOutputs(TIM1, DISABLE);
}

接下来是timer.h的代码

#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"

/***************************************************************************/
#define PWM_TIM_ON()      {TIM_CtrlPWMOutputs(TIM1, ENABLE); TIM_Cmd(TIM1, ENABLE); TIM_Cmd(TIM3, ENABLE);}
#define PWM_TIM_OFF()     {TIM_CtrlPWMOutputs(TIM1, DISABLE); TIM_Cmd(TIM1, DISABLE); TIM_Cmd(TIM3, DISABLE);}

void TIM1_PWM_Init(u16 arr,u16 psc);
void TIM3_Int_Init(u16 arr,u16 psc);
void TIM3_PWM_Init(u16 arr,u16 psc);
#endif

至此,所有编码过程结束。。。

第四章 故事结尾

没啥好说的,直接下进去执行看看对不对,其实调试的过程还是出现的一些状况。比如在LED初始化之前初始化了uart,uart正好也复用了PA9所以一直没有出结果,最后去掉了uart的初始化就好了,那有小司机就问题,我就是想用uart1怎么办,那你就去重映射IO吧。

下载到硬件,执行代码一切如所愿。。。故事结束。

第五章 升华主题

接下来要干嘛?接下来要实现红色led呼吸灯效果,这个可能比较复杂一点,,,就交给你们了。。。哈哈。。。

为什么定义为DBG_LED呢?因为我觉得这个电路和这套代码很适合作为调试的指示器,用最小的电路实现丰富的效果。当然,你也可以根据需求将PA9移植到其他IO。怎么样?没想到两个LED还能这么玩吧~

平淡而颓废的18年过去了,19年会多多输出一些有趣好玩的东西,额,,,不知道这句话会不会打脸,再见了。

猜你喜欢

转载自blog.csdn.net/weixin_34377065/article/details/87100868