【STM32】状态机实现定时器按键消抖,处理单击、双击、三击、长按事件

目录

一、简单介绍

二、模块与接线

三、cubemx配置

四、驱动编写

状态图

按键类型定义

参数初始化/复位

按键扫描

串口重定向

主函数

五、效果展示

六、驱动附录

key.c

key.h

一、简单介绍

        众所周知,普通的机械按键会产生抖动,可以采取硬件上加电容来滤波,也可以考虑用软件来消抖。这里笔者分享一种基于状态机的按键消抖策略,可以实现单击双击三击长按事件的读取。按键时间也可以自己设置。

        这种方法需要消耗掉定时器资源,还有额外的RAM支出。

二、模块与接线

笔者使用STM32单片机来实现这一过程,具体型号为STM32F103CBT6,和常见的最小核心板引脚是一样的,只是容量大一些。

外部按键选择的是51单片机的独立按键,原理图如下

按下按键后,相应的引脚电平就为低。将P30,P31,P32,P33分别连接至单片机的PA1,PA2,PA3,PA4

三、cubemx配置

GPIO口开启对应的按键为输入模式,配置上拉

串口

时钟为72MHz

定时器设置为10ms触发一次

四、驱动编写

状态图

将一个按键从按下前到按下再到松手分成四个状态:无操作、按下、按压、弹起

对应的状态图如下,分别是四个圆形

按键类型定义

如图矩形框内描述,最终键值的确定需要标志位和计数值,因此一个按键结构体应该这样定义

typedef struct 
{
	GPIO_TypeDef * GPIO_Port;		//按键端口
	uint16_t GPIO_Pin;				//按键PIN
	KeyActionType key;				//按键类型
	uint16_t hold_cnt;				//按压计数器
	uint16_t high_cnt;				//高电平计数器
	uint8_t press_flag;				//按压标志
	uint8_t release_flag;			//松手标志
	ButtonActionType buttonAction;	//按键键值
}buttonType;

该工程需要配置的只有一个主函数文件,外加笔者编写的key.c和key.h还有串口重定向的部分 

参数初始化/复位

void Key_ParaInit(buttonType* button)
{
	button->high_cnt = 0;
	button->hold_cnt = 0;
	button->press_flag = 0;
	button->release_flag = 0;
}

按键扫描

代码如下,基本实现了状态图

void Key_Scan(buttonType* button)
{
	switch(button->key)
	{
		case KEY_NULL:
		{
			/* if falling edge captured */
			if(HAL_GPIO_ReadPin(button->GPIO_Port,button->GPIO_Pin) == 0)
			{
				button->key = KEY_DOWN;
			}
			else if(HAL_GPIO_ReadPin(button->GPIO_Port,button->GPIO_Pin) == 1)
			{
				button->key = KEY_NULL;
			}
					
			/* if button is released ,high_time_count++ */
			if(button->release_flag == 1)
			{
				button->high_cnt++;
			}			

			/**********************judge***********************/
			/* if high_time_count is longer than LONG_PRESS_TIME, consider BUTTON_LONG_PRESS */
			if(button->hold_cnt > LONG_PRESS_TIME)
			{
				button->buttonAction = BUTTON_LONG_PRESS;
				Key_ParaInit(button);
			}
			/* if high_time_count is shorter than LONG_PRESS_TIME,but longer than CLICK_MAX_TIME consider INVALID */
			else if(button->hold_cnt < LONG_PRESS_TIME && button->hold_cnt > CLICK_MAX_TIME)
			{
				Key_ParaInit(button);
			}

			/* 
				only the latest press time is in range of [CLICK_MIN_TIME,CLICK_MAX_TIME] can be regarded valid
				if high level time > JUDGE_TIME also means that over the JUDGE_TIME and still dont have button pushed
				we can check the flag value to get button state now
			*/
			else if((button->high_cnt > JUDGE_TIME)&&(button->hold_cnt > CLICK_MIN_TIME && button->hold_cnt < CLICK_MAX_TIME))
			{
				if(button->press_flag ==1)
				{
					button->buttonAction = BUTTON_SINGLE;
				}
				else if(button->press_flag == 2)
				{
					button->buttonAction = BUTTON_DOUBLE;
				}
				else if(button->press_flag == 3)
				{
					button->buttonAction = BUTTON_TRIPLE;
				}
				
				Key_ParaInit(button);
			}
			break;
		}
		
		case KEY_DOWN:
		{
			button->key = KEY_PRESS;
			
			/* as long as falling edge occurring,press_flag++ */
			button->press_flag++;
			
			button->release_flag = 0; 			/* means that the button has been pressed */

			button->hold_cnt = 0;				/* reset hold time count */
			break;
		}
		
		case KEY_PRESS:
		{
			/* when button was kept pressed, hold count++ */
			if(HAL_GPIO_ReadPin(button->GPIO_Port,button->GPIO_Pin) == 0)
			{
				button->key = KEY_PRESS;
				button->hold_cnt++;
			}
			/* when button was released, change state */
			else if(HAL_GPIO_ReadPin(button->GPIO_Port,button->GPIO_Pin) == 1)
			{
				button->key = KEY_UP;
			}
			break;
		}
		
		case KEY_UP:
		{
			button->key = KEY_NULL;
			
			button->release_flag = 1;			/* means that the button is released */

			button->high_cnt = 0;				/* reset hold time count */

			/* if press time is longer than 1s then press_flag-- */
			if(button->hold_cnt > 100)
			{
				button->press_flag--;
			}
			break;
		}
		default:
			break;
	}
}

里面涉及到的宏定义和枚举,都在头文件内给出

double click:
```___________``````````````___________```````````` 
     min< <max    <judge      min< <max     >judge
	
single click:
``````___________`````````
       min< <max  >judge
	   
*/

#define LONG_PRESS_TIME 	     100
#define CLICK_MIN_TIME 		5	/* if key press_cnt time less than this -> invalid click */
#define CLICK_MAX_TIME 		20	/* if key press_cnt time more than this -> invalid click */
#define JUDGE_TIME 			20	/* double click time space */


typedef enum
{
	KEY_NULL,
	KEY_DOWN,
	KEY_PRESS,
	KEY_UP,
}KeyActionType;

typedef enum
{
	BUTTON_NULL,
	BUTTON_SINGLE,
	BUTTON_DOUBLE,
	BUTTON_TRIPLE,
	BUTTON_LONG_PRESS,
}ButtonActionType;

串口重定向

打开usart.c

添加如下代码

int fputc(int ch, FILE *f)
{
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
  return ch;
}

int fgetc(FILE *f)
{
  uint8_t ch = 0;
  HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
  return ch;
}

主函数

在主循环内去读取键值,用定时器来周期扫描按键

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "tim.h"
#include "key.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM2_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	Key_Config();					//配置按键
	HAL_TIM_Base_Start_IT(&htim2); 	//开定时器
	
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  Key_Debug();
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim == &htim2)
	{
		Key_Scan(button);
		Key_Scan(button+1);
		Key_Scan(button+2);
		Key_Scan(button+3);
	}
}
/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

五、效果展示

按下按键,串口打印相应的按键号和键值

六、驱动附录

key.c

#include "key.h"
#include "stdio.h"

buttonType button[4];

void Key_Config()
{
	button[0].GPIO_Port = K1_GPIO_Port;
	button[0].GPIO_Pin = K1_Pin;

	button[1].GPIO_Port = K2_GPIO_Port;
	button[1].GPIO_Pin = K2_Pin;

	button[2].GPIO_Port = K3_GPIO_Port;
	button[2].GPIO_Pin = K3_Pin;

	button[3].GPIO_Port = K4_GPIO_Port;
	button[3].GPIO_Pin = K4_Pin;
}

void Key_ParaInit(buttonType* button)
{
	button->high_cnt = 0;
	button->hold_cnt = 0;
	button->press_flag = 0;
	button->release_flag = 0;
}

void Key_Scan(buttonType* button)
{
	switch(button->key)
	{
		case KEY_NULL:
		{
			/* if falling edge captured */
			if(HAL_GPIO_ReadPin(button->GPIO_Port,button->GPIO_Pin) == 0)
			{
				button->key = KEY_DOWN;
			}
			else if(HAL_GPIO_ReadPin(button->GPIO_Port,button->GPIO_Pin) == 1)
			{
				button->key = KEY_NULL;
			}
					
			/* if button is released ,high_time_count++ */
			if(button->release_flag == 1)
			{
				button->high_cnt++;
			}			

			/**********************judge***********************/
			/* if high_time_count is longer than LONG_PRESS_TIME, consider BUTTON_LONG_PRESS */
			if(button->hold_cnt > LONG_PRESS_TIME)
			{
				button->buttonAction = BUTTON_LONG_PRESS;
				Key_ParaInit(button);
			}
			/* if high_time_count is shorter than LONG_PRESS_TIME,but longer than CLICK_MAX_TIME consider INVALID */
			else if(button->hold_cnt < LONG_PRESS_TIME && button->hold_cnt > CLICK_MAX_TIME)
			{
				Key_ParaInit(button);
			}

			/* 
				only the latest press time is in range of [CLICK_MIN_TIME,CLICK_MAX_TIME] can be regarded valid
				if high level time > JUDGE_TIME also means that over the JUDGE_TIME and still dont have button pushed
				we can check the flag value to get button state now
			*/
			else if((button->high_cnt > JUDGE_TIME)&&(button->hold_cnt > CLICK_MIN_TIME && button->hold_cnt < CLICK_MAX_TIME))
			{
				if(button->press_flag ==1)
				{
					button->buttonAction = BUTTON_SINGLE;
				}
				else if(button->press_flag == 2)
				{
					button->buttonAction = BUTTON_DOUBLE;
				}
				else if(button->press_flag == 3)
				{
					button->buttonAction = BUTTON_TRIPLE;
				}
				
				Key_ParaInit(button);
			}
			break;
		}
		
		case KEY_DOWN:
		{
			button->key = KEY_PRESS;
			
			/* as long as falling edge occurring,press_flag++ */
			button->press_flag++;
			
			button->release_flag = 0; 			/* means that the button has been pressed */

			button->hold_cnt = 0;				/* reset hold time count */
			break;
		}
		
		case KEY_PRESS:
		{
			/* when button was kept pressed, hold count++ */
			if(HAL_GPIO_ReadPin(button->GPIO_Port,button->GPIO_Pin) == 0)
			{
				button->key = KEY_PRESS;
				button->hold_cnt++;
			}
			/* when button was released, change state */
			else if(HAL_GPIO_ReadPin(button->GPIO_Port,button->GPIO_Pin) == 1)
			{
				button->key = KEY_UP;
			}
			break;
		}
		
		case KEY_UP:
		{
			button->key = KEY_NULL;
			
			button->release_flag = 1;			/* means that the button is released */

			button->high_cnt = 0;				/* reset hold time count */

			/* if press time is longer than 1s then press_flag-- */
			if(button->hold_cnt > 100)
			{
				button->press_flag--;
			}
			break;
		}
		default:
			break;
	}
}



void Key_Debug()
{
	
	for(uint8_t i=0;i<4;i++)
	{
		switch(button[i].buttonAction)
		{
			
			case BUTTON_SINGLE:
			{
				button[i].buttonAction = BUTTON_NULL;
				printf("%d->",i);
				printf("BUTTON_SINGLE\r\n");
				break;
			}
			case BUTTON_LONG_PRESS:
			{
				button[i].buttonAction = BUTTON_NULL;
				printf("%d->",i);
				printf("BUTTON_LONG_PRESS\r\n");
				break;
			}
			case BUTTON_DOUBLE:
			{
				button[i].buttonAction = BUTTON_NULL;
				printf("%d->",i);
				printf("BUTTON_DOUBLE\r\n");
				break;
			}
			case BUTTON_TRIPLE:
			{
				button[i].buttonAction = BUTTON_NULL;
				printf("%d->",i);
				printf("BUTTON_TRIPLE\r\n");
				break;
			}
			case BUTTON_NULL:
			{
				button[i].buttonAction = BUTTON_NULL;
				break;
			}
			default:
			{
				break;
			}
		}
	}
	
}

key.h

#ifndef KEY_H
#define KEY_H

#include "tim.h"
#include "main.h"
/*
double click:
```___________``````````````___________```````````` 
     min< <max    <judge      min< <max     >judge
	
single click:
``````___________`````````
       min< <max  >judge
	   
*/

#define LONG_PRESS_TIME 	     100
#define CLICK_MIN_TIME 		5	/* if key press_cnt time less than this -> invalid click */
#define CLICK_MAX_TIME 		20	/* if key press_cnt time more than this -> invalid click */
#define JUDGE_TIME 			20	/* double click time space */


typedef enum
{
	KEY_NULL,
	KEY_DOWN,
	KEY_PRESS,
	KEY_UP,
}KeyActionType;

typedef enum
{
	BUTTON_NULL,
	BUTTON_SINGLE,
	BUTTON_DOUBLE,
	BUTTON_TRIPLE,
	BUTTON_LONG_PRESS,
}ButtonActionType;

typedef struct 
{
	GPIO_TypeDef * GPIO_Port;		//按键端口
	uint16_t GPIO_Pin;				//按键PIN
	KeyActionType key;				//按键类型
	uint16_t hold_cnt;				//按压计数器
	uint16_t high_cnt;				//高电平计数器
	uint8_t press_flag;				//按压标志
	uint8_t release_flag;			//松手标志
	ButtonActionType buttonAction;	//按键键值
}buttonType;

extern buttonType button[4];

void Key_Scan(buttonType*);
void Key_Debug();
void Key_Config();

#endif

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

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

相关文章

【江南大学×朗汀留学】部分留学录取案例合集

朗汀留学 X 江南大学 尽管客观条件如此艰难&#xff0c;朗汀留学的同学们还是斩获众多名校的录取。成绩属于过去&#xff0c;我们继续努力创造更好未来。 以下为我们摘取的江南大学部分学生案例供大家参考&#xff0c;再次恭喜所有获得理想大学offer的学生们&#xff0c;你们…

CAST: Cross-Attention in Space and Time for Video Action Recognition

标题&#xff1a;CAST: 时空交叉注意力网络用于视频动作识别 原文链接&#xff1a;2311.18825v1 (arxiv.org)https://arxiv.org/pdf/2311.18825v1 源码链接&#xff1a;GitHub - KHU-VLL/CASThttps://github.com/KHU-VLL/CAST 发表&#xff1a;NeurIPS-2023&#xff08;CCF A…

Linux第四节--常见的指令介绍集合(持续更新中)

点赞关注不迷路&#xff01;本节涉及初识Linux第四节&#xff0c;主要为常见的几条指令介绍。 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1f44d;&#x1f3fb; 收藏 ✨ 加关注&#x1f440; 期待与你共同进步! 1. more指令 语法&#xff1a;more [选项][文件]…

MySQL文档_下载

可能需要&#xff1a;MySQL下载–》更新版本–》迁移数据库到MySQL 以下都不重要【只要确定好需要安装版本&#xff0c;找到对应的版本下载&#xff0c;安装&#xff0c;设置即可】 下载、安装&#xff1a; Determine whether MySQL runs and is supported on your platform…

电脑刚开机的时候不卡,用一会就变卡顿了…怎么回事?

前言 昨天咱们聊到旧电脑更换了固态硬盘之后&#xff0c;开机就会变快的事情&#xff0c;这个确实是可行且有效的升级电脑办法之一。 看完这篇之后&#xff0c;切莫着急升级电脑硬件配置&#xff0c;因为这里的坑比你想象的还要多。 从机械硬盘测试的数据和固态硬盘的测试数…

【C语言】—— 动态内存管理

【C语言】——动态内存管理 一、动态内存管理概述1.1、动态内存的概念1.2、动态内存的必要性 二、 m a l l o c malloc malloc 函数2.1、函数介绍2.2、应用举例 三、 c a l l o c calloc calloc 函数四、 f r e e free free 函数4.1、函数介绍4.2、应用举例 五、 r e a l l o …

SSL协议

SSL 安全传输协议&#xff08;安全套接层&#xff09; 也叫TLS ---- 传输层安全协议 SSL的工作原理&#xff1a;SSL协议因为是基于TCP协议工作的&#xff0c;通信双方需要先建立TCP会话。因为SSL协议需要进行安全保证&#xff0c;需要协商安全参数&#xff0c;所以也需要建立…

【数据结构】栈和队列专题

前言 上篇博客我们讨论了栈和队列的有关结构&#xff0c;本篇博客我们继续来讨论有关栈和队列习题 这些题算是经典了 &#x1f493; 个人主页&#xff1a;小张同学zkf ⏩ 文章专栏&#xff1a;数据结构 若有问题 评论区见&#x1f4dd; &#x1f389;欢迎大家点赞&#x1f44d…

Oracle 临时表空间的管理

Oracle 临时表空间的管理 临时表空间的处理 1.创建一个新的temporary tablespace; create temporary tablespace tp tempfile ...... size 10m autoextend on; 2.改变数据库的默认临时表空间 alter database default temporary tablespace tp; 3。drop tablespace temp; …

Zoho CRM企业成长的智能引擎,智能化销售自动化

数字化时代&#xff0c;客户体验已成为企业竞争的核心要素。卓豪Zoho CRM&#xff0c;作为全球领先的SaaS云端客户关系管理平台&#xff0c;正引领着一场企业运营模式的变革&#xff0c;助力超过25万家企业跨越180多个国家&#xff0c;实现客户互动与业务增长的无缝对接。让我们…

Verlog-流水灯-FPGA

Verlog-流水灯-FPGA 引言&#xff1a; ​ 随着电子技术的飞速发展&#xff0c;现场可编程门阵列&#xff08;FPGA&#xff09;已成为电子设计自动化&#xff08;EDA&#xff09;领域中不可或缺的组件。FPGA以其高度的灵活性和可定制性&#xff0c;广泛应用于通信、图像处理、工…

【C++】学习笔记——继承_2

文章目录 十二、继承5. 继承与友元6. 继承与静态成员7. 复杂的菱形继承及菱形虚拟继承 未完待续 十二、继承 5. 继承与友元 友元关系不能继承&#xff0c;也就是说父类友元不能访问子类私有和保护成员 。除非子类也设置成友元。 6. 继承与静态成员 父类定义了 static 静态成…

单用户模式破解root密码

目录 一. 破解root密码 1. 查看操作系统版本 2.重启系统&#xff0c;进入grub菜单&#xff0c;选择要使用的内核&#xff0c;按e进入​编辑 3. 找到linux16那一行&#xff0c;把光标移动到最后&#xff0c;添加 init/bin/sh 然后ctrlx保存退出会自动进入系统 4. 进入系统后…

Spring WebFlux:响应式编程

在软件开发领域&#xff0c;随着互联网应用的规模和复杂性不断增加&#xff0c;传统的编程模型逐渐暴露出一些局限性&#xff0c;尤其是在面对高并发、大规模数据流处理等场景时。为了应对这些挑战&#xff0c;响应式编程&#xff08;Reactive Programming&#xff09;应运而生…

强化训练:day9(添加逗号、跳台阶、扑克牌顺子)

文章目录 前言1. 添加逗号1.1 题目描述2.2 解题思路2.3 代码实现 2. 跳台阶2.1 题目描述2.2 解题思路2.3 代码实现 3. 扑克牌顺子3.1 题目描述3.2 解题思路3.3 代码实现 总结 前言 1. 添加逗号   2. 跳台阶   3. 扑克牌顺子 1. 添加逗号 1.1 题目描述 2.2 解题思路 我的写…

ros键盘控制程序teleop_twist_keyboard 键值含义及用法

在机器人仿真中&#xff0c; 经常会用到键盘控制程序teleop_twist_keyboard 对机器人进行控制。但是对各个键值是何种含义&#xff0c; 如何操作并没有任何资料介绍,初次使用时会不知所措。 通过实践&#xff0c; 发现各个键值的作用如下&#xff1a; u-- 向左前方前进 i-- 直…

java-spring 09 下.populateBean (方法成员变量的注入@Autowird,@Resource)

1.在populateBean 方法中的一部分&#xff1a;用于Autowird&#xff0c;Resource注入 // 后处理器已经初始化boolean hasInstAwareBpps hasInstantiationAwareBeanPostProcessors();// 需要依赖检查boolean needsDepCheck (mbd.getDependencyCheck() ! AbstractBeanDefinitio…

现在闪侠惠递寄快递有福利了,千万不要因没把握住而后悔呀!

闪侠惠递平台寄快递现在真的是太便宜了&#xff0c;优惠价格把握不住&#xff0c;后悔都来不及&#xff01;大家可以在闪侠惠递上面寄快递&#xff0c;价格真的非常优惠呢&#xff0c;比咱们平常寄快递的价格都优惠呢&#xff0c;真的&#xff0c;小编都亲自替大家尝试过了呢。…

联软安渡 UniNXG 安全数据交换系统 任意文件读取漏洞复现

0x01 产品简介 联软安渡UniNXG安全数据交换系统,是联软科技自研的业内融合网闸、网盘和DLP的一体机产品,它同时支持多网交换,查杀毒、审计审批、敏感内容识别等功能,是解决用户网络隔离、网间及网内数据传输、交换、共享/分享、存储的理想安全设备,具有开创性意义。 UniN…

千层烤馍,五彩斑斓的甘肃特色美食

甘肃玫瑰千层烤馍是一道具有浓郁地方特色的传统面点&#xff0c;以其独特的口感和精美的外观而闻名。食家巷的千层烤馍更是其中的佼佼者&#xff0c;它采用了优质的原料和传统的制作工艺&#xff0c;让你品尝到最正宗的甘肃味道。 食家巷千层烤馍的制作过程非常讲究&#xff0c…