状态机编程

news2024/11/25 12:52:36

在这里插入图片描述

 

//定义的枚举
typedef enum
{
KEY_UP =1, //按键按下
Edge_Lead=2, //前沿抖动
KEY_DOWN =3, //按键松开
Edge_Back=4, //后沿抖动
} KEY_Status;

主函数:

#include "stm32f4xx.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "timer.h"

//定义了按键状态的结构体
KEY_Status  KEY2_Status;
//10ms定时开关
uint8_t Timer10ms_Flag=0;
uint8_t cnt=0 ;

int main(void)
{
    
    delay_init(168);
    KEY_Init();
    MyGPIO_Init();
    TIM3_Int_Init(400-1,8400-1);//10ms的定时器
    KEY2_Status =KEY_UP ;    //按键的初始状态
    
    while(1)
    {
                //当10ms定时时间到
                if(Timer10ms_Flag==1)
                {
                    Timer10ms_Flag=0;
                    switch(KEY2_Status)
                    {
                        case KEY_UP:
                               if(KEY2==0)  KEY2_Status =Edge_Lead;
                                 cnt=0;
                            break;
                        case Edge_Lead:
                               cnt++;
                                 if(KEY2==1)  KEY2_Status =KEY_UP;
                                 else if(cnt==2)
                                 {
                                     KEY2_Status =KEY_DOWN;
                                     cnt=0;
                                 }    
                            break;
                        case KEY_DOWN:
                               if(KEY2==1)
                               KEY2_Status =Edge_Back;    
                                 cnt=0;
                            break;
                        case Edge_Back:
                                cnt++;
                                    if(KEY2==0) KEY2_Status=KEY_DOWN;
                                    else if(cnt==2)
                                    {
                                        KEY2_Status =KEY_UP;
                                        cnt=0;
                                    }         
                            break;
                        default :
                            break;
                    }
                    
                    if(KEY2_Status ==KEY_DOWN)
                    {
                        LED2_ON();
                    }
                    if(KEY2_Status ==KEY_UP)
                    {
                        LED2_OFF();
                    }
                    
                }         
    }
}
 

1 全自动洗衣机功能分析
下面是一个全自动洗衣机的控制面板:

面板上有4个按键:

电源:控制洗衣机通电与断电
水位:选择洗衣时需要的水位,有1~8个水位
程序:选择不同的洗衣模式,有1~10个模式
01:标准
02:轻柔
03:快速

10:桶风干
启动/暂停:用于启动或暂停洗衣任务
面板上还有一个数码管,用于显示当前的工作状态与剩余时间,可显示的工作模式有:

AA:浸泡
BB:洗涤
CC:漂洗
DD:脱水
本篇,就按照这款洗衣机的操作方式实现对应的洗衣机控制逻辑。需注意的是:

实际的洗衣机有水位检测传感器,本篇中,暂用时间延时模拟水位的增加,且默认开机时水位为0
实际的洗衣机中的洗衣模式,会根据不同的模式自动设置清洗次数与每次清洗的时间以及清洗力度,本篇为了简化起见,清洗模式的设置仅用于区分不同的清洗次数
某些特殊的清洗模式,如单独的脱水,桶风干等,本篇暂不实现
对于状态的显示 ,本篇先以串口打印的实现展示,下篇使用OLED小屏幕来显示不同清洗状态的图标等信息
2 画状态图
根据上面分析的全自动洗衣机的功能,以及我们自己使用洗衣机时的经验,可以画出如下的全自动洗衣机的状态图:

首先是上电开机,洗衣机可能会开机自检,检测洗衣机的各个部件是否正常,次过程很短。

然后就处于空闲状态,此时用户可以设置水位与清洗模式,若不设置,则为默认的水位与洗衣模式。

接着触发开始按键后,就开始清洗了,一般流程就是:加水、清洗、排水、甩干、结束。

根据不同的清洗模式,加水、清洗和排水这3个过程会循环执行一定的次数。

另外,在不同的工作阶段,按下暂停键可以让洗衣任务暂停,再按继续可让洗衣任务继续。

3 编程实现
3.1 多按键检测功能
本篇介绍的洗衣机的按键,仅需要检测按键单击即可,不需要双击与长按功能,因此,可使用之前文章介绍的最基础的按键状态机来为洗衣机状态机提供按键事件。

之前介绍的按键状态机,只有一个按键,本篇需要用到4个按键(除去电源键,3个也可以),因此,需要对按键状态机稍加修改,实现按键状态机的复用。

之前介绍的按键状态机,使用了几个全局变量来表示状态,更合理的做法是将其封装起来:

typedef struct {
  KEY_STATUS keyStatus;    //当前循环结束的(状态机的)状态
  KEY_STATUS nowKeyStatus; //当前状态(每次循环后与pKeyFsmData->keyStatus保持一致)
  KEY_STATUS lastKeyStatus; //上次状态(用于记录前一状态以区分状态的来源)
  int keyIdx;
}KeyFsmData;
注意,额外增加了一个按键索引值,用来告诉状态机要检测哪个按键。

再将原来的按键状态机程序,通过入参的形式将上述定义的结构体传入,并通过函数返回的形式返回按键是否被按下。

这样修改后的按键状态机,就是一个独立的模块了,可以通过传入不同的参数,实现不同按键的检测。

bool key_press_check(KeyFsmData *pKeyFsmData)
{
  bool bPress = false;
    
  switch(pKeyFsmData->keyStatus)
  {
//省略...
对于本篇需要的4个按键的检测,就可以定义4个数据结构体,分别调用4次状态机函数即可,实现代码的复用。

KeyFsmData pKey0FsmData;
KeyFsmData pKey1FsmData;
KeyFsmData pKey2FsmData;
KeyFsmData pKey3FsmData;

void key_check_init(KeyFsmData *pKeyFsmData, int keyIdx)
{
  pKeyFsmData->keyStatus = KS_RELEASE;
  pKeyFsmData->lastKeyStatus = KS_RELEASE;
  pKeyFsmData->nowKeyStatus = KS_RELEASE;
  pKeyFsmData->keyIdx = keyIdx;
}

void multi_key_check_init()
{
  key_check_init(&pKey0FsmData, 0);
  key_check_init(&pKey1FsmData, 1);
  key_check_init(&pKey2FsmData, 2);
  key_check_init(&pKey3FsmData, 3);
}

int g_keyPressIdx = -1;
void multi_key_check()
{
  bool key0 = key_press_check(&pKey0FsmData);
  bool key1 = key_press_check(&pKey1FsmData);
  bool key2 = key_press_check(&pKey2FsmData);
  bool key3 = key_press_check(&pKey3FsmData);
  
  if (key0 | key1 | key2 | key3)
  {
    //printf("key0:%d, key1:%d, key2:%d, key3:%d, \r\n", key0, key1, key2, key3);
    
    if (key0)
    {
      g_keyPressIdx = 0;
    }
    else if (key1)
    {
      g_keyPressIdx = 1;
    }
    else if (key2)
    {
      g_keyPressIdx = 2;
    }
    else if (key3)
    {
      g_keyPressIdx = 3;
    }
  }
}

int get_press_key_idx()
{
  int idx = g_keyPressIdx;
  g_keyPressIdx = -1;
  
  return idx;
}
其中,multi_key_check函数,放到50ms周期的定时器中断服务函数中,使按键状态机程序运行起来。

get_press_key_idx函数,用于洗衣机程序来获取不同按键的按下事件,每次获取后,将按键事件清除(g_keyPressIdx设为无效值-1)

3.2 洗衣功能
按照上面绘制的洗衣机状态图,使用switch-case法编写对应的程序即可:

#define WASHER_STATUS_ENUM(STATUS)\
  STATUS(WS_INIT)/*开机初始化自检*/         \
  STATUS(WS_IDLE)/*空闲(等待模式设置)状态*/ \
  STATUS(WS_ADD_WATER)/*加水状态*/               \
  STATUS(WS_WASH)/*清洗状态*/               \
  STATUS(WS_DRAIN_WATER)/*排水状态*/               \
  STATUS(WS_SPIN_DRY)/*甩干状态*/               \
  STATUS(WS_PAUSE)/*暂停状态*/               \
  STATUS(WS_NUM)/*状态总数(无效状态)*/

typedef enum
{
  WASHER_STATUS_ENUM(ENUM_ITEM)
}WASHER_STATUS;

const char* washer_status_name[] = {
  WASHER_STATUS_ENUM(ENUM_STRING)
};

WASHER_STATUS g_washerStatus = WS_INIT;    //当前循环结束的(状态机的)状态
WASHER_STATUS g_nowWasherStatus = WS_INIT; //当前状态(每次循环后与pKeyFsmData->keyStatus保持一致)
WASHER_STATUS g_lastWasherStatus = WS_INIT; //上次状态(用于记录前一状态以区分状态的来源)

int g_WorkLoopCnt = 0;
int g_WaterLevel = 5; //1~8
int g_WashMode = 2; //暂定为清洗次数:1~3
int g_curWashCnt = 0;

void washer_run_loop()
{
  switch(g_washerStatus)
  {
    /*开机初始化自检*/ 
    case WS_INIT:
      g_washerStatus = washer_do_init();
      break;
    
    /*空闲(等待模式设置)状态*/
    case WS_IDLE:
      g_washerStatus = washer_do_idle();
      break;
    
    /*加水状态*/
    case WS_ADD_WATER:
      g_washerStatus = washer_do_add_water();
      break;
    
    /*清洗状态*/
    case WS_WASH:
      g_washerStatus = washer_do_wash();
      break;
    
    /*排水状态*/
    case WS_DRAIN_WATER:
      g_washerStatus = washer_do_drain_water();
      break;
    
    /*甩干状态*/
    case WS_SPIN_DRY:
      g_washerStatus = washer_do_spin_dry();
      break;
    
    /*暂停状态*/
    case WS_PAUSE:
      g_washerStatus = washer_do_pause();
      break;
    
    default: break;
  }
  
  if (g_washerStatus != g_nowWasherStatus)
  {
    g_lastWasherStatus = g_nowWasherStatus;
    g_nowWasherStatus = g_washerStatus;
    //printf("new washer status:%d(%s)\r\n", g_washerStatus, washer_status_name[g_washerStatus]);
  }
}
这里将洗衣机不同状态时的处理逻辑,都分别使用函数来实现,并将其返回值作为下一个要跳转的状态。

各个状态的处理函数如下:

/*开机初始化自检*/ 
WASHER_STATUS washer_do_init()
{
  WASHER_STATUS nextStatus = WS_INIT;
  
  g_WorkLoopCnt++;
  if (10 == g_WorkLoopCnt) //自检结束
  {
    printf("default water level:%d\r\n", g_WaterLevel);
    printf("default wash mode:%d\r\n", g_WashMode);
    printf("washer idle...\r\n");
    
    g_WorkLoopCnt = 0;
    nextStatus = WS_IDLE;
  }
  
  return nextStatus;
}

/*空闲(等待模式设置)状态*/
WASHER_STATUS washer_do_idle()
{
  WASHER_STATUS nextStatus = WS_IDLE;
  
  const WASHER_KEY key = check_key_press();
  switch(key)
  {
    case W_KEY_START_PAUSE:
      g_WorkLoopCnt = 0;
      nextStatus = WS_ADD_WATER;
      printf("add water...\r\n");
    break;
    
    case W_KEY_WATER_LEVEL:
      g_WaterLevel = (g_WaterLevel == 8) ? 1 : (g_WaterLevel+1);
      printf("set water level:%d\r\n", g_WaterLevel);
    break;
      
    case W_KEY_WASH_MODE:
      g_WashMode = (g_WashMode == 3) ? 1 : (g_WashMode+1);
      printf("set wash mode:%d\r\n", g_WashMode);
    break;
    
    default: break;
  }
  
  return nextStatus;
}

/*加水状态*/
WASHER_STATUS washer_do_add_water()
{
  WASHER_STATUS nextStatus = WS_ADD_WATER;
  
  const WASHER_KEY key = check_key_press();
  if (key == W_KEY_START_PAUSE)
  {
    nextStatus = WS_PAUSE;
    printf("[%s] pause...\r\n", __func__);
  }
  else
  {
    g_WorkLoopCnt++;
    if (g_WaterLevel == (g_WorkLoopCnt / 10)) //加水结束
    {
      g_WorkLoopCnt = 0;
      nextStatus = WS_WASH;
      printf("[%s] done\r\n", __func__);
      printf("wash...\r\n");
    }
  }
  
  return nextStatus;
}

/*清洗状态*/
WASHER_STATUS washer_do_wash()
{
  WASHER_STATUS nextStatus = WS_WASH;
  
  const WASHER_KEY key = check_key_press();
  if (key == W_KEY_START_PAUSE)
  {
    nextStatus = WS_PAUSE;
    printf("[%s] pause...\r\n", __func__);
  }
  else
  {
    g_WorkLoopCnt++;
    if (6 == (g_WorkLoopCnt / 10)) //清洗结束
    {
      g_WorkLoopCnt = 0;
      nextStatus = WS_DRAIN_WATER;
      printf("[%s] done\r\n", __func__);
      printf("drain water...\r\n");
    }
  }
  
  return nextStatus;
}

/*排水状态*/
WASHER_STATUS washer_do_drain_water()
{
  WASHER_STATUS nextStatus = WS_DRAIN_WATER;
  
  const WASHER_KEY key = check_key_press();
  if (key == W_KEY_START_PAUSE)
  {
    nextStatus = WS_PAUSE;
    printf("[%s] pause...\r\n", __func__);
  }
  else
  {
    g_WorkLoopCnt++;
    if (3 == (g_WorkLoopCnt / 10)) //排水结束
    {
      printf("[%s] done\r\n", __func__);
      g_curWashCnt++;
      printf("current wash and drain count:%d(target:%d)\r\n", g_curWashCnt, g_WashMode);
      g_WorkLoopCnt = 0;
      if (g_WashMode == g_curWashCnt)
      {
        printf("spin dry...\r\n");
        nextStatus = WS_SPIN_DRY;
      }
      else
      {
        printf("add water...\r\n");
        nextStatus = WS_ADD_WATER;
      }
    }
  }
  
  return nextStatus;
}

/*甩干状态*/
WASHER_STATUS washer_do_spin_dry()
{
  WASHER_STATUS nextStatus = WS_SPIN_DRY;
  
  const WASHER_KEY key = check_key_press();
  if (key == W_KEY_START_PAUSE)
  {
    nextStatus = WS_PAUSE;
    printf("[%s] pause...\r\n", __func__);
  }
  else
  {
    g_WorkLoopCnt++;
    if (100 == g_WorkLoopCnt) //甩干结束
    {
      g_WorkLoopCnt = 0;
      nextStatus = WS_IDLE;
      printf("[%s] done\r\n", __func__);
      printf("enter idle mode\r\n");
    }
  }
  
  return nextStatus;
}

/*暂停状态*/
WASHER_STATUS washer_do_pause()
{
  WASHER_STATUS nextStatus = WS_PAUSE;
  
  const WASHER_KEY key = check_key_press();
  if (key != W_KEY_NULL)
  {
    switch (g_lastWasherStatus)
    {
      case WS_ADD_WATER:   nextStatus = WS_ADD_WATER;   printf("resume...\r\n"); break;
      case WS_WASH:        nextStatus = WS_WASH;        printf("resume...\r\n"); break;
      case WS_DRAIN_WATER: nextStatus = WS_DRAIN_WATER; printf("resume...\r\n"); break;
      case WS_SPIN_DRY:    nextStatus = WS_SPIN_DRY;    printf("resume...\r\n"); break;
      default: break;
    }
  }
  
  return nextStatus;
}
对于按键的获取,这里定义了几个对应的功能按键,并从按键状态机函数中获取按键索引,再转为洗衣机程序所需的对应功能按键:

typedef enum
{
  W_KEY_NULL,        //没有按键按下
  W_KEY_POWER,       //电源键按下
  W_KEY_WATER_LEVEL, //水位键按下
  W_KEY_WASH_MODE,   //清洗模式键按下
  W_KEY_START_PAUSE  //启动/暂停键按下
}WASHER_KEY;

WASHER_KEY check_key_press()
{
  WASHER_KEY washerKey = W_KEY_NULL;
  int idx = get_press_key_idx();
  if (idx != -1)
  {
    switch(idx)
    {
      case 0: washerKey = W_KEY_POWER; break; //电源键按下
      case 1: washerKey = W_KEY_WATER_LEVEL; break; //水位键按下
      case 2: washerKey = W_KEY_WASH_MODE; break; //清洗模式键按下
      case 3: washerKey = W_KEY_START_PAUSE; break; //启动/暂停键按下
      default: break;
    }
    //printf("%s idx:%d -> washerKey:%d\r\n", __func__, idx, washerKey);
  }
  
  return washerKey;
}
洗衣机状态机主程序,可以放到main函数中,每隔100ms调用一次,使其运行起来:

int main(void)
{  
  delay_init();
  KEY_Init();
  uart_init(115200);
  TIM3_Int_Init(500-1,7200-1); //调用定时器使得50ms产生一个中断

  printf("hello\r\n");
  
  while(1)
  {
    washer_run_loop();
    delay_ms(100);
  }
}
3.3 测试
将代码烧写到STM32板子中,通过3个按键(电源键暂不使用)操作,并通过串口打印,查看全自动洗衣机的运行情况:

4 总结
本篇实现了一款全自动洗衣机的基础洗衣控制流程,可实现不同水位与清洗次数的设置,以及任务的暂停与继续。此外,通过对之前按键状态机的进一步优化修改,实现了按键状态机的复用,实现多个按键的检测。下篇文章将进一步进行功能优化,添加OLED小屏幕实现不同状态的可视化展示。

状态机
状态机在软件编程中非常重要,一个思路清晰而且高效的程序,必然有状态机的身影浮现。比如在按键命令解析程序中,本来是在状态1中,触发一个按键后切换到状态2,再触发另一个按键切换到状态3,或者返回到状态1。按键的击键过程也是一种状态的切换,也可以看着是一个状态机,一个按键的击键过程包括:按下、抖动、闭合、抖动和释放等状态。我们只要把这些状态机的思想想办法用程序表示出来就可以了。

3、按键的状态机实现
我们这里用状态机是为解决问题的,那么我们就要从问题本身去思考。为了实现按键扫描,达到按键短按和长按的功能,可以根据一个按键从按下按键到释放按键的整个过程将按键分为4个状态:

S0:等待按键按下

S1:按键按下

S2:等待按键短按释放

S3:等待按键长按释放

假设按键按下为低电平“0”,按键未按下为高电平“1”,按键的整个过程我们就可以通过状态转移图表示出来,如图1所示。

图1:按键的状态转移图

首先,按键的初始状态为S0,当检测到输入为1时,表示按键没有按下,保持S0;当输入为0时,表示按键按下,状态转入S1。在S1状态中,检测输入信号是否为0,如果为0,执行按键程序转入S2;如果为1,表示之前的按键操作是干扰信号,回到S0。在S2状态中,如果输入信号是1,则回到S0,表示按键短按已经释放;如果按键没有释放,输入为0时,就开始计时,计时没有结束前一直在S2,当计时结束了,转入S3,表示按键一直按着,为长按功能,在S2计时过程中,输入从0变为1也会回到S0。在S3状态中,输入信号为1,返回S0,表示按键长按释放;输入信号为0,执行相应的按键程序,也可以计时,等计时结束执行按键程序,达到按键连击的功能。这就是采用状态机进行按键检测达到短按与长按的整个过程。

下面以四个按键接在P1的P1.7、P1.6、P1.5、P1.4,设计状态机按键扫描程序。

程序代码如下:

#defineS00//状态0

#defineS11//状态1

#defineS22//状态2

#defineS33//状态3

voidkey()

{staticunsignedcharstate=S0,key_time;

unsignedcharkey;

key=P1&0xf0;//屏蔽P1低四位

switch(state)//检测状态

{caseS0://状态0

if(key!=0xf0)state=S1;break;//判断输入是否为0,为0转入状态1

caseS1://状态1

if(key==0xf0)state=S0;//判断输入是否为1,为1返回状态0

else//否则,转入状态2,执行按键程序

{state=S2;

switch(key)

{case0xe0:/*按键1执行程序*/

break;

case0xd0:/*按键2执行程序*/break;

case0xb0:/*按键3执行程序*/break;

case0x70:/*按键4执行程序*/break;}}

break;

caseS2://状态2

if(key==0xf0)state=S0;//判断输入是否为1,为1返回状态0

elseif(++key_time==100){key_

time=0;state=S3;}break;

//否则开始计时,计时结束转入状态3

caseS3://状态3

if(key==0xf0)state=S0;//判断输入是否为1,为1返回状态0

elseif(++key_time==5)//否则开始计时,计时结束按键连击

{key_time=0;

switch(key){

case0xe0:break;

case0xd0:break;

case0xb0:break;

case0x70:break;}

}break;}}

4、中断处理按键消抖
通常使用的按键都是机械弹性按键,也就是轻触开关。机械按键在触点的闭合和断开的过程中会产生抖动,一个按键在按下时不会立刻稳定的导通,在释放时也一样,不会一下子就断开,在按下和释放瞬间都会有一连串的抖动现象。按键的抖动时间有按键的机械特性决定,一般情况为5ms~10ms。这种抖动人是感觉不出来的,但是单片机的运行速度是微秒级的,这里可以设计一个定时中断来检测按键的状态,通过定时中断来消除按键抖动问题。因此可以把定时器的时间设置为10ms,每隔10ms进入一次中断检测一次按键的状态。

5、总结
 以按键功能,介绍了状态机的原理与按键状态机实例,实现按键单击、双击、长按等状态的检测。本文介绍的这种以状态机来实现按键检测的方法,与一般的按键检测方法相对比,能完成案件的多种状态的检测,实现按键的短按和长按功能。采用状态机编写的按键程序也大大的改进了按键消抖对CPU运行时间消耗的问题。程序代码简单,维护方便,适用范围广。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/456692.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

QMS-云质说质量 - 8 颠覆你的认知,中小型企业数字化转型更容易成功

数字化转型,不但不遥远,而且似乎离我们每个人的生活还非常近。尤其是近几年,出于政府号召与扶持,市场竞争以及企业自身发展需要等各方面原因,越来越多的企业已经开始或者正在准备进行数字化转型。即使是规模一两百人的…

MATLAB如何自定义颜色图(colormap)

MATLAB有一套自己的颜色库,常用的都有,但是数量不算太多。我们有时候需要用到一些MATLAB没有的colormap,比如Python的Matplotlib就有很多的colormap,我们也有可能需要自己来定义一些渐变的颜色。本片笔记主要是介绍colormap如何自…

在vite中使用mockjs, vite中使用vite-plugin-mock

相信前端同学都会碰见类似的问题,就是页面可能很快写完了,但是接口同学还在缓慢设计表中! 这个时候咳咳,你就可以去摸鱼了或者看小说了 但实际上可不能这样哦,要老老实实做个打工人 步入正题了 在vite中有个mock的插…

SpringCloud学习6(Spring Cloud Alibaba)断路器Sentinel熔断降级

文章目录 服务熔断降级Sentinel高并发请求模拟(这里我们使用contiperf来进行测试)修改tomcat配置最大线程数引入测试依赖编写测试代码 服务雪崩服务雪崩的容错方案(隔离、超时、限流、熔断、降级)隔离机制:超时机制&am…

混淆电路(GC)

基本概念 在混淆电路框架下,任意功能函数可被表示为一个与门和异或门组成的布尔电路,协议的参与方由生成方(Garbler)和计算方(Evaluator)组成。 **大致的流程:**生成方生成密钥并加密查找表&am…

淘宝天猫数据查询(天猫智能手环数据分析)

近几年,中国智能可穿戴设备市场规模不断增长,也取得了傲人的成绩。从可穿戴设备市场整体发展来看,智能手环是一大主角。智能手环市场接受度和认可度的逐渐提升,为各类厂商提供了更多机会,同时这也蕴含了更多市场增量空…

分享两个有意思的登录界面

1.带有浮动占位符和灯光按钮的登录界面 先上效果: 代码如下: <!DOCTYPE html> <html lang="en"> <head>

L2-2 天梯赛的赛场安排

作者 陈越 单位 浙江大学 天梯赛使用 OMS 监考系统&#xff0c;需要将参赛队员安排到系统中的虚拟赛场里&#xff0c;并为每个赛场分配一位监考老师。每位监考老师需要联系自己赛场内队员对应的教练们&#xff0c;以便发放比赛账号。为了尽可能减少教练和监考的沟通负担&#…

this.$set的正确使用

this.#set(obj, key, value) 我们在项目开发的过程中&#xff0c;经常会遇到这种情况&#xff1a;为data中的某一个对象添加一个属性 <template><div class"hello"><button click"setMessage">添加属性</button>{{ student.name…

开发IM(即时通讯)服务端

首先讲讲IM&#xff08;即时通讯&#xff09;技术可以用来做什么&#xff1a;可以说几乎所有高实时性的应用场景都需要用到IM技术。 本篇将带大家从零开始搭建一个轻量级的IM服务端&#xff0c;麻雀虽小&#xff0c;五脏俱全&#xff0c;我们搭建的IM服务端实现以下功能&#x…

gpt.4.0-gpt 国内版

gpt 使用 GPT&#xff08;Generative Pre-trained Transformer&#xff09;是一种预训练的语言模型&#xff0c;可用于多种自然语言处理任务&#xff0c;如情感分析、文本分类、文本生成等。下面是使用GPT的一些步骤和建议&#xff1a; 确定任务和数据集&#xff1a;首先&…

选择美国虚拟主机需注意的安全问题

在选择美国虚拟主机时&#xff0c;安全性应该是您首要关注的问题。虚拟主机通常是网站托管的最便宜和最方便的方式之一&#xff0c;但也存在安全问题。在本文中&#xff0c;我们将讨论一些您应该注意的安全问题&#xff0c;并提供一些解决方案来保护您的网站。 一、了解虚拟主机…

Linux 配置与磁盘管理

目录 物理设备的命名规则Linux中硬盘分区部分Linux在分区上建立文件系统挂载Linux硬盘管理工具LVM逻辑卷管理器物理卷、卷组合逻辑卷的建立 逻辑卷的使用LVM整体创建流程硬盘配额配置&#xff08;针对用户或者群组使用的空间&#xff09; 物理设备的命名规则 Linux系统内核中的…

分析trace 知识点分析的很全面

Total time breakdown&#xff1a;依据关键tag拆分&#xff0c;比如binderapplication/activitystart/doframe 找出时间差异大tag的cpu status &#xff1a;Running/Runnable/Sleeping/Uninterruptible sleep Running:SW-diff-check 或 cpu能力(大小核或频率&#xff09; Runna…

v-for比v-if优先级更高?面试官:回去等通知吧

大厂面试题分享 面试题库 前后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 web前端面试题库 VS java后端面试题库大全 前言 v-if和v-for哪个优先级更高呢&#xff1f;这是面试官常常问到的一个问题&#xff0c;…

七、vue-基础之条件渲染

一、条件渲染 在某些情况下&#xff0c;我们需要根据当前的条件决定某些元素或者组件是否渲染&#xff0c;这个时候我们就需要进行条件判断了。 Vue提供了下面的指令来进行条件判断&#xff1a; v-ifv-elsev-else-ifv-show &#xff08;1&#xff09;需求demo体验 我们直接…

Win11的两个实用技巧系列之内存不足导致永劫无间闪退解决方法、Win11本地安全机构保护误报修复方法

Win11内存不足导致永劫无间闪退解决方法 Win11内存不足导致永劫无间闪退怎么办&#xff1f;其实解决方法很简单&#xff0c;本文就为大家带来了详细的解决方法&#xff0c;感兴趣的朋友一起看看吧 Win11内存不足导致永劫无间闪退怎么办&#xff1f;永劫无间出现闪退的情况可能…

python练手小案例——采集二手车数据

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 本次案例亮点: 1、系统分析目标网页 2、html标签数据解析方法 3、海量数据一键保存 环境介绍: 在开始写我们的代码之前&#xff0c;我们要准备好运行代码的程序 Python 3.8.8 | Anaconda, Inc. &#xff1a;解释器 Pych…

物联网|ARM|Keil安装|MDK|增加V5编译器|物联网开发系列课程之零基础玩转Cortex-M系列CPU-学习笔记(1)

文章目录 第一阶段-课程背景及简介问题一 什么是物联网问题二 嵌入式系统在物联网中的角色问题三 不一样的嵌入式系统 课程介绍课程内容提要&#xff08;学什么)课程特色课程要求&#xff08;怎么学)课程目的(怎么用)课程约定 浅谈ARM什么是ARMARM体系架构CPU的特点ARM体系架构…

04-24 每日一题 1163. 按字典序排在最后的子串 学习笔记反思

不晓得脑袋的灵活性是不是和精力的充沛程度相关&#xff0c;看到红色的困难&#xff0c;感觉自信都没了 题目描述 给你一个字符串 s &#xff0c;找出它的所有子串并按字典序排列&#xff0c;返回排在最后的那个子串 示例 1 :::success 输入&#xff1a;s “abab” 输出&…