STM32:GPIO

1 GPIO引脚电路

  

  IO_port输入输出模式的配置 等于 对IO_port电路连接方式的配置;

  IO_port的弱上下拉电阻默认30-50kΩ;单个GPIO的电流最大值为20mA;全部IO的电流总值约120mA;具体见数据手册;

  1.1 输入模式

    上拉输入:使能弱上拉电阻的连接,断开下拉电阻的连接;以防输入电压过高保护内部电路;

    下拉输入:断开上拉电阻的连接,使能弱下拉电阻的连接;以防输入电压过低保护内部电路;

    浮空输入:默认浮空输入,不使能上下拉电阻,浮空输入信号,较为准确反应外部输入信号;  

  1.2 输出模式

    推挽输出:默认推挽输出,将P-MOS管和N-MOS管以推挽方式连接;通过两个MOS管的导通与截止来输出高低电平;配置上下拉电阻不使能;

           特点是既可以消耗负载的灌电流,也可以向负载输出拉电流,开关时间快;

         两个MOS管工作原理相同,作用相反,误差中和后,可以减小电路误差积累,增加电路稳定;

    开漏输出:P-MOS管始终截止,通过N-MOS管结合上下拉电阻,控制输出高低电平;配置上下拉电阻同时使能;

           特点是输出的高电平由弱上拉电阻决定,输出电流小,负载驱动能力弱;输出低电平时可以消耗负载的灌电流,驱动能力稳定;

         如果觉得开漏输出的输出驱动能力弱,可以自己外加一个强上拉电阻,这样负载驱动能力就变强了;

         “线与”逻辑:将多个开漏输出并连至一个上拉电阻,当其中一个输出低电平时,其他输出也被接到地了;iic_sda就是这样的;  

  1.4 电平范围

    仅对于STM32而言,其兼容的TTL电平和CMOS电平范围如下:

      TTL电平:大于2.4V(in:2.0v)为高电平;低于0.8V(in:1.2v)为低电平;噪声容限0.4V;TTL为流控器件,电阻小功耗大驱动强;

      CMOS电平:大于0.7V为高电平;小于0.2V为低电平;噪声容限0.2V;功耗较小,电压阈值宽;

      STM32H7的GPIO对TTL电平和CMOS电平的兼容问题 - STM32H7 - 硬汉嵌入式论坛 - Powered by Discuz! (armbbs.cn)

2 GPIO ports

  GPIO全称 general purpose input and output ports,通用输入输出端口;

  STM32一共有7组GPIO port:GPIOA-GPIOG,每组GPIO port 有16个 pin;每组GPIO port都有一组寄存器;

  GPIO寄存器的控制单位是GPIO port,而不是pin;所以寄存器的最小处理单位是一个16位的字长(0xFFFF);

GPIO_TypeDef()
/*stm32f10x.h  
*GPIO都属于APB2总线,使用的时候要使能APB2总线的时钟源;
*uint32_t 使用需要 #include <stdint.h>*/

typedef struct
{
  __IO uint32_t CRL;
  __IO uint32_t CRH;
  __IO uint32_t IDR;
  __IO uint32_t ODR;
  __IO uint32_t BSRR;
  __IO uint32_t BRR;
  __IO uint32_t LCKR;
} GPIO_TypeDef;

#define PERIPH_BASE           ((uint32_t)0x40000000)

#define APB1PERIPH_BASE       PERIPH_BASE
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)

#define GPIOA_BASE            (APB2PERIPH_BASE + 0x0800)
#define GPIOB_BASE            (APB2PERIPH_BASE + 0x0C00)
#define GPIOC_BASE            (APB2PERIPH_BASE + 0x1000)
#define GPIOD_BASE            (APB2PERIPH_BASE + 0x1400)
#define GPIOE_BASE            (APB2PERIPH_BASE + 0x1800)
#define GPIOF_BASE            (APB2PERIPH_BASE + 0x1C00)
#define GPIOG_BASE            (APB2PERIPH_BASE + 0x2000)

#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB               ((GPIO_TypeDef *) GPIOB_BASE)
#define GPIOC               ((GPIO_TypeDef *) GPIOC_BASE)
#define GPIOD               ((GPIO_TypeDef *) GPIOD_BASE)
#define GPIOE               ((GPIO_TypeDef *) GPIOE_BASE)
#define GPIOF               ((GPIO_TypeDef *) GPIOF_BASE)
#define GPIOG               ((GPIO_TypeDef *) GPIOG_BASE)
/*core_cm3.h*/
#define __IO  volatile   

  2.1 CRL端口配置低寄存器 control register low,CRH端口配置高寄存器 control register high;

    

GPIO_InitTypeDef()
/***stm32f10x_gpio.h 定义了CRH和CRL寄存器需要的参数;***/

typedef struct
{
  uint16_t GPIO_Pin;                /*用16位bit的每一位分别表示一个引脚*/            
  GPIOSpeed_TypeDef GPIO_Speed;     /*用2位bit来表示输出模式的最大速度,寄存器中的MODE[1:0]*/ 
  GPIOMode_TypeDef GPIO_Mode;       /*用2位bit来表示输入输出的电阻连接,寄存器中的CNF[3:2]*/
}GPIO_InitTypeDef;

#define GPIO_Pin_0                 ((uint16_t)0x0001)  /*0000 0000 0000 0001b*/
#define GPIO_Pin_1                 ((uint16_t)0x0002)  /*0000 0000 0000 0010b*/
#define GPIO_Pin_2                 ((uint16_t)0x0004)  /*0000 0000 0000 0100b*/
#define GPIO_Pin_3                 ((uint16_t)0x0008)  /*0000 0000 0000 1000b*/
#define GPIO_Pin_4                 ((uint16_t)0x0010)  /*0000 0000 0001 0000b*/
#define GPIO_Pin_5                 ((uint16_t)0x0020)  /*0000 0000 0010 0000b*/
#define GPIO_Pin_6                 ((uint16_t)0x0040)  /*0000 0000 0100 0000b*/
#define GPIO_Pin_7                 ((uint16_t)0x0080)  /*0000 0000 1000 0000b*/
#define GPIO_Pin_8                 ((uint16_t)0x0100)  /*0000 0001 0000 0000b*/
#define GPIO_Pin_9                 ((uint16_t)0x0200)  /*!< Pin 9 selected */
#define GPIO_Pin_10                ((uint16_t)0x0400)  /*!< Pin 10 selected */
#define GPIO_Pin_11                ((uint16_t)0x0800)  /*!< Pin 11 selected */
#define GPIO_Pin_12                ((uint16_t)0x1000)  /*!< Pin 12 selected */
#define GPIO_Pin_13                ((uint16_t)0x2000)  /*!< Pin 13 selected */
#define GPIO_Pin_14                ((uint16_t)0x4000)  /*!< Pin 14 selected */
#define GPIO_Pin_15                ((uint16_t)0x8000)  /*!< Pin 15 selected */
#define GPIO_Pin_All               ((uint16_t)0xFFFF)  /*!< All pins selected */
#define IS_GPIO_PIN(PIN) ((((PIN) & (uint16_t)0x00) == 0x00) && ((PIN) != (uint16_t)0x00))

typedef enum
{ 
  GPIO_Speed_10MHz = 1,     /*output reg MODE[1:0]*/
  GPIO_Speed_2MHz,          /*output reg MODE[1:0]*/
  GPIO_Speed_50MHz          /*output reg MODE[1:0]*/
}GPIOSpeed_TypeDef;
#define IS_GPIO_SPEED(SPEED) (((SPEED) == GPIO_Speed_10MHz) || ((SPEED) == GPIO_Speed_2MHz) || \
                              ((SPEED) == GPIO_Speed_50MHz))
                              
typedef enum
{ GPIO_Mode_AIN = 0x0,                /*0000 0000b [4]0 input [3:2]CNF*/
  GPIO_Mode_IN_FLOATING = 0x04,       /*0000 0100b [4]0 input [3:2]CNF*/
  GPIO_Mode_IPD = 0x28,               /*0010 1000b [4]0 input [3:2]CNF,[5]下拉*/
  GPIO_Mode_IPU = 0x48,               /*0100 1000b [4]0 input [3:2]CNF,[6]上拉*/
  GPIO_Mode_Out_OD = 0x14,            /*0001 0100b [4]1 output,[3:2]CNF*/
  GPIO_Mode_Out_PP = 0x10,            /*0001 0000b [4]1 output,[3:2]CNF*/
  GPIO_Mode_AF_OD = 0x1C,             /*0001 1100b [4]1 output,[3:2]CNF*/
  GPIO_Mode_AF_PP = 0x18              /*0001 1000b [4]1 output,[3:2]CNF*/
}GPIOMode_TypeDef; 
#define IS_GPIO_MODE(MODE) (((MODE) == GPIO_Mode_AIN) || ((MODE) == GPIO_Mode_IN_FLOATING) || \
                            ((MODE) == GPIO_Mode_IPD) || ((MODE) == GPIO_Mode_IPU) || \
                            ((MODE) == GPIO_Mode_Out_OD) || ((MODE) == GPIO_Mode_Out_PP) || \
                            ((MODE) == GPIO_Mode_AF_OD) || ((MODE) == GPIO_Mode_AF_PP))
#define IS_GET_GPIO_PIN(PIN) (((PIN) == GPIO_Pin_0) || ((PIN) == GPIO_Pin_1) ||((PIN) == GPIO_Pin_2) || \
                              ((PIN) == GPIO_Pin_3) || ((PIN) == GPIO_Pin_4) || ((PIN) == GPIO_Pin_5) || \
                              ((PIN) == GPIO_Pin_6) || ((PIN) == GPIO_Pin_7) || ((PIN) == GPIO_Pin_8) || \
                              ((PIN) == GPIO_Pin_9) || ((PIN) == GPIO_Pin_10) || ((PIN) == GPIO_Pin_11) || \
                              ((PIN) == GPIO_Pin_12) ||((PIN) == GPIO_Pin_13) || ((PIN) == GPIO_Pin_14) || \
                              ((PIN) == GPIO_Pin_15))

typedef enum
{ Bit_RESET = 0,
  Bit_SET
}BitAction;

    在usart外设使用的时候是AF_PP_output加上IN_FLOATING_input;在spi外设使用的时候,把MI、MO都配置为AF_PP了,这咋也行呢?

    因为对于双向复用端口,规定将其配置成复用输出,外设输入驱动器默认会被自动配置成浮空输入模式,中文手册是建议自己主动配置的;

GPIO_Init( )
/***stm32f10x_gpio.c***
***配置相应port的CRL和CRH;
***这里的输入输出模式在代码中新设置了一个bit来判断,但是在赋值寄存器的时候,芯片大概率是通过MODE[1:0]的值来物理区分的;***/
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
  uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
  uint32_t tmpreg = 0x00, pinmask = 0x00;
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));
  assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));
    
 //currentmode保留了GPIO_Mode[3:0];  
 currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F); 
  //如果GPIO_Mode[4]为1,表示为输出模式;    
  if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
  { 
	 /*if(输出模式),将CNF[3:2]和MODE[1:0]的信息保存到currentmode[3:0]*/
	assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
    currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;//currentmode或上GPIO_Speed[1:0];
  }
  
  /*以下部分为CRL Configuration*/
    
  //if(是低8位pin);   
  if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)		
  {
    tmpreg = GPIOx->CRL;、//temreg存放CRL寄存器的信息;
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)//pinpos为几,表示引脚几;		
    {
      pos = ((uint32_t)0x01) << pinpos;					
      currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;//当pos与结构体pin脚都为1,则引脚为pos执行if语句;
      if (currentpin == pos)
      {
        pos = pinpos << 2;//pos:引脚对应的CRL配置位,左移<<2 == *4;
        pinmask = ((uint32_t)0x0F) << pos;//pinmask:引脚对应的CRL[3:0]置1,其他位清0后与0为0;
        tmpreg &= ~pinmask;//temreg中对应引脚的[3:0]清0
        tmpreg |= (currentmode << pos);//temreg中对应引脚的[3:0]配置成currentmode[3:0]
    
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)//if(输入连下拉电阻)
        {
          GPIOx->BRR = (((uint32_t)0x01) << pinpos);//BRR的对应bit置0,连接下拉电阻;
        }
        else
        {
          if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU) //if(输入连上拉电阻)
          {
            GPIOx->BSRR = (((uint32_t)0x01) << pinpos); //BSRR的对应bit置1,连接上拉电阻;
          }
        }
      }
    }
    GPIOx->CRL = tmpreg;  
  }
  
/*---------------------------- GPIO CRH Configuration ------------------------*/
  /* Configure the eight high port pins */
  if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
  {
    tmpreg = GPIOx->CRH;
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
      pos = (((uint32_t)0x01) << (pinpos + 0x08));
      /* Get the port pins position */
      currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
      if (currentpin == pos)
      {
        pos = pinpos << 2;
        /* Clear the corresponding high control register bits */
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
        /* Write the mode configuration in the corresponding bits */
        tmpreg |= (currentmode << pos);
        /* Reset the corresponding ODR bit */
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
          GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
        /* Set the corresponding ODR bit */
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
        {
          GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
      }
    }
    GPIOx->CRH = tmpreg;
  }
}

  2.2 LCKR lock register 锁定寄存器

    锁存对应端口的CRL、CRH寄存器的配置,对应的CRL、CRH配置将会持续到下次系统复位信号来临;

    

GPIO_PinLockConfig()
/**
  * @brief  Locks GPIO Pins configuration registers.
  * @param  GPIOx: where x can be (A..G) to select the GPIO peripheral.
  * @param  GPIO_Pin: specifies the port bit to be written.
  *   This parameter can be any combination of GPIO_Pin_x where x can be (0..15).
  * @retval None
  */
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  uint32_t tmp = 0x00010000;
  
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GPIO_PIN(GPIO_Pin));
  
  tmp |= GPIO_Pin;
  /* Set LCKK bit */
  GPIOx->LCKR = tmp;
  /* Reset LCKK bit */
  GPIOx->LCKR =  GPIO_Pin;
  /* Set LCKK bit */
  GPIOx->LCKR = tmp;
  /* Read LCKK bit*/
  tmp = GPIOx->LCKR;
  /* Read LCKK bit*/
  tmp = GPIOx->LCKR;
}

  2.3  IDR input data register 端口输入数据寄存器,ODR output data register 端口输出数据寄存器

    

GPIO_ReadInputDataBit()
/*以下代码位于stm32f10x_gpio.c中*1 读取IDR寄存器某一位的值,即pin值;*2 读取IDR寄存器的值,即port值;*/
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  uint8_t bitstatus = 0x00;
  
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GET_GPIO_PIN(GPIO_Pin)); 

  /*先读取整个IDR寄存器,然后通过&来读取bit,IDR寄存器的最小处理单位是16bit*/
  if ((GPIOx->IDR & GPIO_Pin) != (uint32_t)Bit_RESET)
  {
    bitstatus = (uint8_t)Bit_SET;
  }
  else
  {
    bitstatus = (uint8_t)Bit_RESET;
  }
  return bitstatus;
}
GPIO_ReadInputData()
 uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx)
{
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  
  return ((uint16_t)GPIOx->IDR);
}
GPIO_ReadOutputDataBit()
/*以下代码位于stm32f10x_gpio.c中;*/
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  uint8_t bitstatus = 0x00;
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GET_GPIO_PIN(GPIO_Pin)); 

   /*先读取整个ODR寄存器,然后通过&来读取bit,ODR寄存器的最小处理单位是16bit*/
  if ((GPIOx->ODR & GPIO_Pin) != (uint32_t)Bit_RESET){
    bitstatus = (uint8_t)Bit_SET;
  }
  else{
    bitstatus = (uint8_t)Bit_RESET;
  }
  return bitstatus;
}
GPIO_ReadOutputData()
 
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx)
{
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));   
  return ((uint16_t)GPIOx->ODR);
}
GPIO_Write()
 
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal)
{
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  GPIOx->ODR = PortVal;
}

  2.4 BSRR bit set/reset register 端口置位/复位寄存器,BRR bit reset register 端口复位寄存器;

    

GPIO_SetBits( )
 void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GPIO_PIN(GPIO_Pin));
  
  GPIOx->BSRR = GPIO_Pin;
}
GPIO_ResetBits( )
 void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GPIO_PIN(GPIO_Pin));
  
  GPIOx->BRR = GPIO_Pin;
}
GPIO_WriteBit( )
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal)
{
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GET_GPIO_PIN(GPIO_Pin));
  assert_param(IS_GPIO_BIT_ACTION(BitVal)); 
  
  /*引脚置1,通过置1BSRR对应位;引脚清0,通过置1BRR对应位;
  *虽然BSRR和BRR都可以用来配置单bit位,但是它们也是通过16bit长度来设置的*/
  if (BitVal != Bit_RESET)
  {
    GPIOx->BSRR = GPIO_Pin;
  }
  else
  {
    GPIOx->BRR = GPIO_Pin;
  }
}

3 GPIO的复用和重映射寄存器

  3.1 GPIO的复用

    各种外设的复用功能,输入输出的配置举例具体见中文参考手册8.1.11小节;

    外设输入通常配置成浮空输入,外设输出通常配置为复用推挽、复用开漏;

    GPIO口复用之后,GPIO的硬件就连接到外设的DR了,要是外设没使能则IO口输出不定值;

  3.2 GPIO的重映射

    重映射功能具体见<中文参考手册>8.3和8.4小节;

    8.3小节主要是列出了外设的重映射引脚;8.4小节是外设的重映射寄存器的配置;

    3.1.1 外设的重映射功能需要通过AFIO_MAPR寄存器来映射到GPIO口;然后配置GPIO口复用该外设;这时候就可以使用该外设了;

    3.1.2 芯片为GPIO的复用外设提供了16个可设置的中断,对应16个引脚号;

      AFIO_EXTICRx中断寄存器配置端口号;EXTICR1配置引脚[3:0]的端口号,EXTICR4配置引脚[15:12]的端口号;

    3.1.3 外设应该有外设自己的中断函数的吧,这里为什么又为复用的外设提供了16个中断呢?这些中断对应哪些中断处理函数呢?

  3.3 GPIO的位带操作

    将目标寄存器的特定bit重映射到bit-band位带区域的对应地址上,对位带区域对应32位地址的操作等于对目标寄存器对应位的操作;

    3.3.1 范围

      [ 0x2000 0000 - 0x200F FFFC ],SRAM的1MBytes寻址范围,共有8Mbits物理存储空间;

      [ 0x2200 0000 - 0x23FF FFFC ],位带区域地址;32MBytes寻址范围,共有256Mbits物理存储空间;

                      膨胀后原先寄存器里的每一bit现在都有专门一个32bit空间可存储;

      [ 0x4000 0000 - 0x400F FFFC ],外设的1MBytes寻址范围,共有8Mbits物理存储空间;

      [ 0x4200 0000 - 0x43FF FFFC ],位带区域地址;32MBytes寻址范围,共有256Mbits物理存储空间;

                      膨胀后原先寄存器里的每一bit现在都有专门一个32bit空间可存储;

    3.3.2 公式

      bit_band_addr = bit_band_base_addr + byte_offset*32 + bit_offset*4 ;

        bit_band_addr :          bit_band位带区映射地址;

        bit_band_base_addr : bit_band位带区基地址,外设bit_band基地址0x4200 0000;

        byte_offset:               目标寄存器地址偏移量;*32表示bit_band存储空间膨胀了32倍;

        bit_offset:                  目标寄存器bit存储空间的所在位数;*4是因为32bit存储空间寻址4个寄存器对齐,那不就是4地址对齐嘛;

    3.3.3 地址对应demo

      bit_band32bits地址内的数据LSB有效;

      

4 通用GPIO的示例代码

main()
 #include "delay.h"

int main(void)
{ 
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitTypeDef GPIO_Struct;

    //portA_pin1    input;     
    GPIO_Struct.GPIO_Pin = GPIO_Pin_1;             
    GPIO_Struct.GPIO_Mode =GPIO_Mode_IPD ;     
    GPIO_Init(GPIOA, &GPIO_Struct); 
    
    //portA_pin2    output;                        
    GPIO_Struct.GPIO_Pin = GPIO_Pin_2; 
    GPIO_Struct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Struct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOA, &GPIO_Struct);                                          
    while(1)
    {
        GPIO_SetBits(GPIOA,GPIO_Pin_2);
        delay_ms(100);
        GPIO_ResetBits(GPIOA,GPIO_Pin_2);
        delay_ms(100);
    } 
}

5 小结

  内核的函数接口标准都是CMSIS,了解了GPIO外设的原理,也就了解了其他外设是如何封装的;

  GPIO使用时先确定是否为外设复用;目的是确定输入输出数据是给外设DR处理,还是GPIO的DR处理;

  然后确定IO的输入输出模式;目的是通过软件配置,选择端口在芯片内部的电路连接方式;

  过一段时间重新来看,于是删改了一下,感觉自己写的也太冗长了,很简单的功能我为什么要写的这么繁琐细碎呢;

  之所以现在觉得太繁琐了是因为知道重点在哪里了,可是一开始没有头绪只能通过繁琐细碎来堆积问题范围;

  如果重新写一遍也只是重复一遍而已,大约没有什么变化;

posted @ 2020-05-16 17:55  caesura_k  阅读(3314)  评论(0编辑  收藏  举报