状态机编程

在这里插入图片描述

 

//定义的枚举
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.mfbz.cn/a/13930.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript—javaEE

文章目录 1.关于JavaScript2.引入的方式3.输入输出4.语法4.1变量4.2基本数据类型4.3运算符4.4数组4.5函数4.6对象 5.dom5.1获取元素5.2操作元素5.3表单控件5.4样式:style属性5.5模仿和服务端交互 6.ajax6.1概念6.2作用6.3Ajax代码6.4Ajax发get请求6.5Ajax发post请求…

学成在线笔记+踩坑(4)——【媒资管理模块】上传图片,Nacos+Gateway+MinIO

导航: 【黑马Java笔记踩坑汇总】JavaSEJavaWebSSMSpringBoot瑞吉外卖SpringCloud黑马旅游谷粒商城学成在线牛客面试题 目录 1. 媒资管理模块简介 1.1 模块介绍 1.2 业务流程 1.2.1 上传课程图片 1.2.2 上传视频 1.2.3 处理视频 1.2.4 审核媒资 1.2.5 绑定媒…

Redis原理

Redis原理 数据结构 动态字符串SDS Redis中key是字符串,value是字符串或字符串集合。不过redis没有直接使用C语言的字符串。因为C中字符串存在问题:①获取字符串长度需要运算②非二进制安全③不可修改。 //c语言,声明字符串: …

字节岗位薪酬体系曝光,看完感叹:不服真不行

曾经的互联网是PC的时代,随着智能手机的普及,移动互联网开始飞速崛起。而字节跳动抓住了这波机遇,2015年,字节跳动全面加码短视频,从那以后,抖音成为了字节跳动用户、收入和估值的最大增长引擎。 自从字节…

媒体宣传的优势与重要性

传媒如春雨,润物细无声,大家好,我是51媒体网胡老师。 媒体宣传日益成为企业和品牌宣传推广的重要手段,媒体的宣传报道更有权威性,能够帮助品牌进行背书,更有权威性,另外媒体的报道在搜索引擎中…

智能文案改写工具-智能改写工具免费

智能写作机器人 智能写作机器人,这是一种让人类写作变得更加简单的创新技术。它的出现,为内容生产领域带来了巨大的进步,不仅提高了人们的写作效率,还让优质的内容更容易被产生和共享。现在,让我们来了解一下智能写作…

Windows环境下NVM安装后Node/NPM命令无法使用

问题:Windows环境下安装nvm后,使用nvm安装node,无法使用node相关命令。 解决方案:注意安装的时候有两个路径,第一个是nvm所在的路径,第二个是nodejs所在的路径,大家需要在对应的目录下找到路径…

Python爬虫实战——获取电影影评

Python爬虫实战——获取电影影评 前言第三方库的安装示例代码效果演示结尾 前言 使用Python爬取指定电影的影评, 注意:本文仅用于学习交流,禁止用于盈利或侵权行为。 操作系统:windows10 家庭版 开发环境:Pycharm Co…

Linux嵌入式uboot使用tftp网络启动加载zImage、设备树

文章目录 一、前言二、Linux U-boot 相关命令(1)help 命令(2)printenv 命令(3)setenv 函数(4)saveenv 函数 三、tftp启动linux内核步骤(1)进入u-boot模式&…

vue:生成二维码 qrcode、vue-qr(二维码中间可带logo)

一、方法一 qrcode qrcode - npm 1.1、安装 yarn add qrcode 1.2、页面引入 import QRCode from qrcode; 1.3、方法里边使用 getQRCodeUrl(){ QRCode.toDataURL(hello world,{color: {dark:"#010599FF",light:"#FFBF60FF"}}).then((url) > {// 获…

基于Html+Css的图片展示25

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

Linux+云服务器

目录 前言 一、Linux介绍 二、Linux 环境搭建 2.1 云服务器 2.2 XShell 终端 三、Linux 常用命令 3.1操作目录的命令 3.1.1 ls 【list的缩写】 双击某个目录 3.1.2 pwd 【print working directory的缩写】打印当前所处地址 3.1.3 cd 【change directory的缩写】切…

yolov5训练自己的目标检测模型

yolov5训练自己的目标检测模型 1.克隆项目并配置环境 1.1克隆项目 进入GitHub下载yolov5源码 点此进入 选择分支v5.0,并下载源码 anaconda激活相应环境 activate pytorch进入项目存放的地址 E: cd yolov5-master1.2 yolov5项目结构 ├── data:主…

Java版本工程管理系统软件源码 自主研发,工程行业适用

Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下: 首页 工作台:待办工作、消息通知、预警信息,点击可进入相应的列表 项目进度图表:选择(总体或单个)项目显示…

Zimbra 远程代码执行漏洞(CVE-2019-9670)漏洞分析

Zimbra 远程代码执行漏洞(CVE-2019-9670)漏洞分析 漏洞简介 Zimbra是著名的开源系统,提供了一套开源协同办公套件包括WebMail,日历,通信录,Web文档管理和创作。一体化地提供了邮件收发、文件共享、协同办公、即时聊天等一系列解决…

集合专题·拔高·壹

文章目录 1 Collection单列集合、Map双列集合1.1 Collection单列集合1.1.1 Collection单列集合及其实现类1.1.1.1 list集合与Array数组1.1.1.1.1 ArrayList1.1.1.1.2 LinekdList1.1.1.1.2 Vector1.1.1.1.2.1 ArrayList、Vector (线程安全)的区别是什么1.…

【elasticsearch部署】

安装elasticsearch 1.部署单点es1.1.创建网络1.2.加载镜像1.3.运行 2.部署kibana2.1.部署2.2.DevTools 3.安装IK分词器3.1.在线安装ik插件(较慢)3.2.离线安装ik插件(推荐)1)查看数据卷目录2)解压缩分词器安…

基于web的电动车租赁管理系统C#+asp.net+sqlserver

具体功能如下:个人信息管理:实现登陆后对个人信息进行修改和查看的功能。 修改登录密码:实现登陆后对个人密码进行修改的功能。 申请租车订单:客户用户登陆后可以申请租车订单。同时可以查看租赁订单信息。 售后评价管理&#xff…

【PR 基础】设置上下黑白边的两种方法

方法1 点击 文件-》新建-》旧版标题 点击确定 点击矩形工具 利用矩形工具框选出上下黑白边 款选完成后点击关闭 将刚创建的字幕拖入轨道 可以修改其持续时长与视频时长保持一致 如果想要修改字幕可以双击来修改 比如可以将颜色改为黑色 方法2 点击号,再选择安全边…

C语言入门篇——函数篇

目录 1、什么是函数 2、函数的分类 2.1库函数 2.2自定义函数 3、函数的参数 3.1实际参数(实参) 3.2形式参数(形参) 4、函数的调用 4.1传值调用 4.2传址调用 5、函数的嵌套调用和链式访问 5.1嵌套调用 5.2链式访问 6、…
最新文章