嵌入式开发十四:按键输入实验

       前面两个实验,我们介绍了 STM32F4 的 IO 口作为输出的使用,这一次,我们将向大家介绍如何使用 STM32F4 的 IO 口作为输入用。我们将利用板载的 4 个按键,来控制板载的两个 LED 的亮灭和蜂鸣器。通过本次的学习,你将了解到 STM32F4 的 IO 口作为输入口 的使用方法。

目录

一、实现的功能

二、 按键与输入数据寄存器介绍

2.1独立按键简介

2.2 GPIO 端口输入数据寄存器(IDR)

三、硬件设计

四、 程序设计

4.1创建工程模板

4.2工程中创建对应的文件

4.3添加文件路径

4.4添加相应的外设固件库和所需文件

​4.5程序流程图

4.5.1 编写LED0和LED1初始化函数驱动代码

4.5.2  编写Beep蜂鸣器初始化函数

4.5.3 编写按键初始化函数

4.5.4 编写按键扫描函数

4.5.5 编写延时函数

4.5.6  程序主函数

五、下载验证


一、实现的功能

         通过探索者 STM32F4 开发板上载有的 4 个按钮(KEY_UP、 KEY0、KEY1 和 KEY2),来控制板上的 2 个 LED(DS0 和 DS1)和蜂鸣器,其中 KEY_UP 控 制蜂鸣器,按一次叫,再按一次停;KEY2 控制 DS0,按一次亮,再按一次灭;KEY1 控制 DS1, 效果同 KEY2;KEY0 则同时控制 DS0 和 DS1,按一次,他们的状态就翻转一次。

二、 按键与输入数据寄存器介绍

2.1独立按键简介

        按键是一种电子开关,使用时轻轻按开关按钮就可使开关接通,当松开手时, 开关断开。我们开发板上使用的按键及内部简易图如下图所示:

       按键管脚两端距离长的表示默认是导通状态,距离短的默认是断开状态,如 果按键按下,初始导通状态变为断开,初始断开状态变为导通。几乎每个开发板都会板载有独立按键,因为按键用处很多。常态下,独立按键是断开的, 按下的时候才闭合。每个独立按键会单独占用一个 IO 口,通过 IO 口的高低电平判断按键的状态。但是按键在闭合和断开的时候,都存在抖动现象,即按键在闭合时不会马上就稳定的连接, 断开时也不会马上断开。这是机械触点,无法避免。通常的按键所用开关为机械弹性开关, 当机械触点断开 、闭合时,电压信号 如下图所示:

       由于机械点的弹性作用,按键开关在闭合时不会马上稳定的接通,在断开时也不会一下子断开,因而在闭合和断开的瞬间均伴随着一连串的抖动。抖动时间的长短由按键的机械特性决定的,一般为 5ms 到 10ms。按键稳定闭合时间的长短则由操作人员的按键动作决定的,一般为零点几秒至数秒。按键抖动会引起按键被误读多次。为了确保 CPU 对按键的一次闭合仅作一次处理(即采样稳定闭合阶段),必须进行消抖。 消抖方法分为硬件消抖和软件消抖,我们常用软件的方法消抖。

  1. 软件消抖:方法很多,最简单的是延时消抖。检测到按键按下后,一般进行 10ms 延时,用于跳过抖动的时间段,如果消抖效果不好可以调整这个 10ms 延时,因为不同类 型的按键抖动时间可能有偏差。待延时过后再检测读取按键状态,如果没有按下,那我们就判断这是抖动或者干扰造成的;如果按键还是按下状态,那么我们就认为这是按键真的按下了。对按键释放的判断同理。
  2. 硬件消抖:利用 RC 电路的电容充放电特性来对抖动产生的电压毛刺进行平滑出来,从而实现消抖,但是成本会更高一点,本着能省则省的原则,我们推荐使用软件消抖即可。

2.2 GPIO 端口输入数据寄存器(IDR)

       本实验我们将会用到 GPIO 端口输入数据寄存器,下面来介绍一下。 该寄存器用于存储 GPIOx 的输入状态,它连接到施密特触发器上,IO 口外部的电平信号经过触发器后,模拟信号就被转化成 0 和 1 这样的数字信号,并存储到该寄存器中。寄存器描述如图所示。

该寄存器低 16 位有效,分别对应每一组 GPIO 的 16 个引脚。当 CPU 访问该寄存器,如果对应的某位为 0(IDRy=0),则说明该 IO 口输入的是低电平,如果是 1(IDRy=1),则表示输入的是高电平,y=0~15。对于我们使用固件库编程,STM32F4 的 IO 口做输入使用的时候,是通过调用函数 GPIO_ReadInputDataBit()来读取 IO 口的状态的,也就是读取该寄存器的值。

三、硬件设计

本实验用到的硬件资源有:

LED0和LED1 以及蜂鸣器和 STM32F4 的连接在前面都已经分别介绍了,独立按键硬件部分的原理图,如图所示,在探索者 STM32F4 开发板上的按键KEY_UP 连接在 PA0 上, KEY0 连接在 PE4 上、KEY1 连接在 PE3 上、KEY2 连接在 PE2 上。如图 8.2.1 所示:

这里需要注意的是:

     KEY0、KEY1 和 KEY2 是低电平有效的,而 KEY_UP 是高电平有效的,并且外部都没有上下拉电阻,所以,需要在 STM32F4 内部设置上下拉。 这里的有效指的是:按键按下时,它是处于高电平还是低电平,很明显,从按键的原理图可知,对于KEY0、KEY1 和 KEY2 ,当按键按下时,GPIO 引脚的输入状态为低电平(按键所在的电路不通,引脚接地)。因此,他们是低电平有效,并且,我们在设置这三个端口模式时,应该上拉,默认是高电平,当按键按下,是低电平,这样便可以检测按键是否按下;对于KEY_UP,当按键被按下的时候,引脚的输入状态为高电平 ,(按键所在的电路导通,引脚接到电源)。因此,他们是高电平有效,并且,我们在设置这个端口模式时,应该下拉,默认是低电平,当按键按下,是高电平,这样便可以检测按键是否按下;总结:只要我们检测引脚的输入电平,即可判断按键是否被按下。 

四、 程序设计

4.1创建工程模板

     直接复制创建好的库函数模板, 在此模板上进行程序开发。将复制过来的模板文件夹重新命名为“实验三:按键输入实验 ”。打开此文件夹,在Project目录下新建一个文件夹,命名为:MyKey,用于存放按键的驱动程序,因为本次实验涉及到LED、Beep因此,同样需要建立和前两个实验一样的文件夹,存放对应的驱动程序。创建好后,目录如下所示:

4.2工程中创建对应的文件

      在工程下创建对应的文件夹,同时新建对应的源文件和头文件,创建好后如下图所示:


4.3添加文件路径

       要想工程在编译阶段能够找到创建的文件,必须要加入文件路径,详细步骤之前已经介绍过,路径添加完毕后,如下图所示:

4.4添加相应的外设固件库和所需文件

     本次实验并没有用到额外的外设,我们就是通过STM32F407上的GPIOF口作为输入口和输出口,输出高电平,蜂鸣器发声,输出低电平,蜂鸣器不发声(IO口驱动蜂鸣器);输出低电平,LED灯点亮,输出低电平,LED灯熄灭;输入高低电平可判断按键是否被按下。


4.5程序流程图

         程序流程图能帮助我们更好的理解一个工程的功能和实现的过程,对学习和设计工程有很好的主导作用。本实验的程序流程图如下:

4.5.1 编写LED0和LED1初始化函数驱动代码

      由于后面我们需要利用按键来控制实现不同的功能,KEY2 控制 DS0,按一次亮,再按一次灭;KEY1 控制 DS1, 效果同 KEY2;KEY0 则同时控制 DS0 和 DS1,按一次,他们的状态就翻转一次。所以我们必须要首先对它们进行初始化,设置PF9和PF10的GPIO口的端口模式。这与我们之前跑马灯实验初始化函数相同。

在 myled.h文件夹下编写如下代码:

#ifndef __MYLED_H
#define __MYLED_H

void LED_Init(void);  //LED初始化函数

#endif

在 myled.c文件夹下编写如下代码:

#include "stm32f4xx.h"                  // Device header
#include "myled.h"


void LED_Init(void)
{
    //第一步:使能GPIOF两个口的时钟 
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能 GPIOF 时钟
	
   //第二步:GPIOF9,F10 初始化设置
   GPIO_InitTypeDef GPIO_InitStructure;
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;//LED0 和 LED1 对应 IO 口
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
   GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化 GPIO

   //第三步:设置灯的初始状态
   GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);//GPIOF9,F10 设置高电平,灯灭

}

4.5.2  编写Beep蜂鸣器初始化函数

      由于后面我们需要利用按键来控制实现不同的功能,其中 KEY_UP 控 制蜂鸣器,按一次叫,再按一次停;所以我们必须要首先对它们进行初始化,设置PF8的GPIO口的端口模式.

在 mybeep.h文件夹下编写如下代码:

#ifndef __MYBEEP_H__
#define __MYBEEP_H__

void BEEP_Init(void);   //蜂鸣器初始化函数

#endif

在 mybeep.c文件夹下编写如下代码:

#include "stm32f4xx.h"                  // Device header
#include "mybeep.h"

void BEEP_Init(void)
{
	//第一步:开启时钟
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能 GPIOF 时钟
    //第二步:初始化蜂鸣器对应引脚 GPIOF8
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化 GPIO
	
    GPIO_ResetBits(GPIOF,GPIO_Pin_8); //蜂鸣器对应引脚 GPIOF8 拉低,此时不会发声(初始状态)
    
}

4.5.3 编写按键初始化函数

      我们后面需要检测按键是否被按下,从而执行不同的操作,实现不同的功能,通过上面查看原理图我们知道,有四个按键,在探索者 STM32F4 开发板上的按键KEY_UP 连接在 PA0 上, KEY0 连接在 PE4 上、KEY1 连接在 PE3 上、KEY2 连接在 PE2 上。此时GPIO口需要用作输入口,通过检测对应GPIO口的高低电平(通过标准库函数读取即可),从而判断按键是否被按下,因此,我们首先需要对按键进行初始化;

在 mykey.h文件夹下编写如下代码:

#ifndef __MYKEY_H__
#define __MYKEY_H__
#include <stdint.h>

//对按键操作函数做了下面的定义
#define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) //PE4
#define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3) //PE3 
#define KEY2 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2) //PE2
#define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) //PA0


//按键进行宏定义增加程序可读性
#define KEY0_PRES 1
#define KEY1_PRES 2
#define KEY2_PRES 3
#define WKUP_PRES 4


void KEY_Init(void);//按键初始化函数
uint8_t KEY_Scan(uint8_t mode); //按键扫描函数


#endif

      需要注意的是:为提高代码的可读性,为了后续对按键进行便捷的操作,我们为按键操作函数做了下面的定义,此外我对按键的键值同样使用了宏定义。

//对按键操作函数做了下面的定义
#define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) //PE4
#define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3) //PE3 
#define KEY2 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2) //PE2
#define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) //PA0

     通过这种方式,我们后面直接使用KEY0、KEY1、KEY2、WK_UP相当于调用相应端口读取电平函数,KEY0、KEY1、KEY2和WK_UP分别是读取对应按键状态的宏定义。用GPIO_ReadInputDataBit() 函数实现,该函数的返回值就是 IO 口的状态,返回值是枚举类型,取值 0 或者 1。看起来更加直观。

//按键进行宏定义增加程序可读性
#define KEY0_PRES 1
#define KEY1_PRES 2
#define KEY2_PRES 3
#define WKUP_PRES 4

    KEY0_PRES、KEY1_PRES、KEY2_PRES 和 WKUP_PRES 则是按键对应的四个键值宏定 义标识符。同样使得代码更加直观,而不是数字1 2 3 4 ,直接代表按键。

 在 mykey.c文件夹下编写如下代码:

      KEY_Init()函数用来初始化按键的端口及时钟。要知道按键是否按下,就需要读取按键所对应的 IO 口电平状态,因此我们需要把 GPIO 配置为输入模式,并且需要把 KEY_UP 按键对应的GPIOA_Pin_0设置为下拉模式,其他几个GPIO口,PE4、  PE3、  PE2需要设置为上拉模式,前面我们分析过。

#include "stm32f4xx.h"                  // Device header
#include "mykey.h"
#include "mydelay.h"

//按键初始化函数:四个按键对应4个GPIO口
void KEY_Init(void)
{
	//第一步:开启时钟,使能 GPIOA,GPIOE 时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);
	
	//第二步:设置端口模式  
	//step1.对于PE2、PE3、PE4应设置为输入上拉模式
	GPIO_InitTypeDef Struct;
	Struct.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;//KEY0 KEY1 KEY2 对应引脚
    Struct.GPIO_Mode = GPIO_Mode_IN;//普通输入模式
    Struct.GPIO_Speed = GPIO_Speed_100MHz;//100M
    Struct.GPIO_PuPd = GPIO_PuPd_UP;//上拉,默认高电平
    GPIO_Init(GPIOE, &Struct);//初始化 GPIOE2,3,4
	
	//step2.对于PA0应设置为输入下拉模式
	GPIO_InitTypeDef Struct1;//按键WK_UP 对应引脚 PA0
	Struct1.GPIO_Pin = GPIO_Pin_0;
    Struct1.GPIO_Mode = GPIO_Mode_IN;//普通输入模式
    Struct1.GPIO_Speed = GPIO_Speed_100MHz;//100M
    Struct1.GPIO_PuPd = GPIO_PuPd_DOWN ;//下拉,默认低电平
    GPIO_Init(GPIOA, &Struct1);//初始化 GPIOA0

}

4.5.4 编写按键扫描函数

      要知道哪个按键被按下,就需要编写按键扫描函数

同样在 mykey.c文件夹下编写如下代码:

      mode:   具体含义如下:

  1. 0, 不支持连续按(当按键按下不放时, 只有第一次调用会返回键值, 必须松开以后, 再次按下才会返回其他键值)
  2. 1, 支持连续按(当按键按下不放时, 每次调用该函数都会返回键值)
uint8_t KEY_Scan(uint8_t mode)
{
	static uint8_t key=1;//按键按松开标志
    if(mode)
	{	
		key=1; 
	}	
	
    if(key&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1))  //任何一个按键按下
    {
       My_Delay_ms(10);//去抖动
       key=0;      
       if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)==0)  //按键0被按下
		   return 1;
       else if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)==0)//按键1被按下
		   return 2;
       else if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)==0)//按键2被按下
		   return 3;
       else if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1)//按键WK_UP被按下
		   return 4;
    }
	else if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)==1&&GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)==1&&GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)==1&&GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==0)
	{
		key=1;
	}		
    return 0;// 无按键按下
}

         key_scan 函数用于扫描这 4 个 IO 口是否有按键按下。key_scan 函数,支持两种扫描方式, 通过 mode 参数来设置。

  1. 当 mode 为 0 的时候,key_scan 函数将不支持连续按,扫描某个按键,该按键按下之后必须要松开,才能第二次触发,否则不会再响应这个按键,这样的好处就是可以防止按一次多次触发,而坏处就是在需要长按的时候比较不合适。
  2. 当 mode 为 1 的时候,函数是支持 连续扫描的,即使按键未松开,在函数内部有 if(mode==1)这条判断语句,因此 key 始终是等于 1 的,所以可以连续扫描按键,当按下某个按键,会一直返回这个按键的键值,这样做的好处是可以很方便实现连按操作。

        有了 mode 这个参数,大家就可以根据自己的需要,选择不同的方式。函数内的 My_Delay_ms(10)即为软件消抖处理,通常延时 10ms 即可。 KEY_Scan 函数还带有一个返回值,如果未有按键按下,返回值即为 0,否则返回值即为对应按键的键值,如 KEY_UP_PRESS、KEY0_PRESS、KEY1_PRESS、KEY2_PRESS,这都是头文件内定义好的宏,方便大家记忆和使用。函数内定义了 一个 static 变量,所以该函数不是一个可重入函数(静态局部变量在函数进行下一次调用时不会重新初始化,直接使用上一次调用函数后的值)。还有一点要注意的就是该函数按键的扫描是有优先级的,因为函数内用了 if...else if...else 格式,所 以最先扫描处理的按键是 KEY_UP,其次是 KEY0,然后是 KEY1,最后是 KEY2。如果需要将其优先级设置一样,那么可以全部用 if 语句。

4.5.5 编写延时函数

  利用系统节拍定时器可以实现精准延时(后期博客详细总结),因此我们实现了三个延时任意时间的函数,后续项目使用直接引入头文件,然后直接调用相应的函数即可。

在 mydelay.h文件夹下编写如下代码:

#ifndef __MYDELAY_H__
#define __MYDELAY_H__
#include <stdint.h>

void My_Delay_us(uint32_t num); //延时任意微秒
void My_Delay_ms(uint32_t num); //延时任意毫秒
void My_Delay_s(uint32_t num);  //延时任意秒

#endif

在 mydelay.c文件夹下编写如下代码: 

#include "stm32f4xx.h"                  // Device header
#include "mydelay.h"
 
 
//延时num微秒
void My_Delay_us(uint32_t num)
{
    while(num--)
    {
        SysTick ->CTRL = (1 << 0);
    
        SysTick ->CTRL &= ~(1<<2);
    
        SysTick ->CTRL &= ~(1<<1);
    
        SysTick ->VAL = 0x0;
    
        SysTick ->LOAD = 21;   //1秒 21000000HZ,1毫秒 21000HZ  1微秒 21 HZ
    
        while(!(SysTick ->CTRL & (1<<16)));
        SysTick ->CTRL = ~(1<<0);
    }
}
 
 
//延时num毫秒,1毫秒等于1000微秒
void My_Delay_ms(uint32_t num)
{
    while(num--)
    {
        My_Delay_us(1000);
    }
}
 
 
//延时num秒,1秒等于1000毫秒
void My_Delay_s(uint32_t num)
{
    while(num--)
    {
        My_Delay_ms(1000);
    }
}

4.5.6  程序主函数
 

      首先是打开外设时钟。接下来调用 led_init 来初始化 LED 灯,调用 beep_init 函数初始化蜂鸣器,调用 key_init 函数初始化按键。最后在无限循环里面扫描获取键值,扫描函数传入的参数值为 0,即 mode=0, 所以这里只对它单次按键操作,将扫描函数返回后的键值保存在变量 key 内,接着用键值判断哪个按键按下,如果有按键按下则翻转相应的灯或翻转蜂鸣器,如果没有按键按下则延时 10ms。编写代码如下:

      这里用到了一个电平翻转函数 GPIO_ToggleBits ();,一个按键被连续按下两次,那么电平便会翻转,从而控制灯的翻转和蜂鸣器的打开和关闭。

#include "stm32f4xx.h"                  // Device header
#include "mykey.h"
#include "mydelay.h"
#include "myled.h"
#include "mybeep.h"


int main(void)
{
	uint8_t  key; //保存键值   
    LED_Init(); //初始化 LED 端口PF9和PF10
    BEEP_Init(); //初始化蜂鸣器端口PF8
    KEY_Init(); //初始化与按键连接的硬件接口
	while(1)
	{
		key=KEY_Scan(0); //得到键值
    if(key)
    { 
	    switch(key)
       { 
		   case WKUP_PRES:            //控制蜂鸣器开和关
		   GPIO_ToggleBits (GPIOF,GPIO_Pin_8);
           break;
		   
           case KEY0_PRES:           //控制 LED0 翻转
		   GPIO_ToggleBits (GPIOF,GPIO_Pin_9);
           break;
		   
           case KEY1_PRES:          //控制 LED1 翻转
		   GPIO_ToggleBits (GPIOF,GPIO_Pin_10);
           break;
		   
           case KEY2_PRES:        //同时控制 LED0,LED1 翻转 
		   GPIO_ToggleBits (GPIOF,GPIO_Pin_9);
		   GPIO_ToggleBits (GPIOF,GPIO_Pin_10);
           break;
       }
    }
  else 
  {
	        My_Delay_ms(10); //延时10ms
		
  }

     }  

}
	

五、下载验证

我们先来看看编译结果,如图所示:

      可以看到 0 错误,0 警告,编译通过接下来,大家就可以下载验证了。这里我们使用 DAP 仿真器下载。下载完之后,运行结果如图所示,可以看到我们可以按 KEY0、KEY1、KEY2 来看看 LED 灯的变化或者按 KEY_UP 看看蜂鸣器的变化,和我们预期的结果一致,符合预期设计。

按键输入

         至此,我们的本次的学习就结束了。学习了 STM32F407 的 IO 作为输入的使用方法, 在前面的 GPIO 输出的基础上又学习了一种 GPIO 使用模式,大家可以回顾前面跑马灯实验介绍的 GPIO 的八种模式类型巩固 GPIO 的知识。

相关推荐

  1. 20240311按键输入实验

    2024-05-16 14:32:05       18 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-05-16 14:32:05       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-05-16 14:32:05       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-05-16 14:32:05       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-05-16 14:32:05       18 阅读

热门阅读

  1. 外挂知识库的论文总结(后续还会更新)

    2024-05-16 14:32:05       12 阅读
  2. Python代码实现求n以内最大的k个素数

    2024-05-16 14:32:05       12 阅读
  3. ControlNet 学习笔记

    2024-05-16 14:32:05       13 阅读
  4. AI工作原理及核心机制

    2024-05-16 14:32:05       10 阅读
  5. 漫谈:C C++ 嵌套包含与前置声明

    2024-05-16 14:32:05       12 阅读
  6. 【postgresql】PostgreSQL中的pgrowlocks插件介绍

    2024-05-16 14:32:05       12 阅读
  7. 机器学习 - 朴素贝叶斯

    2024-05-16 14:32:05       11 阅读
  8. 解决el-dialog弹框出现后页面滚动条可滚动问题

    2024-05-16 14:32:05       14 阅读
  9. nginx中,location匹配规则解析

    2024-05-16 14:32:05       12 阅读
  10. ubuntu 修改网卡名

    2024-05-16 14:32:05       8 阅读
  11. .net 框架基础(一) 字符、字符串

    2024-05-16 14:32:05       12 阅读