当前位置: 首页 > news >正文

STM32实现按键单击、双击、长按、连按功能,使用状态机,无延时,不阻塞

常见的按键判定程序,如正点原子按键例程,只能判定单击事件,对于双击、长按等的判定逻辑较复杂,且使用main函数循环扫描的方式,容易被阻塞,或按键扫描函数会阻塞其他程序的执行。使用定时器设计状态机可以规避这一问题。

本文在博主 老子姓李! 程序的基础上添加连按功能,整合双击功能并补充双击二次按下消抖,添加可调消抖时间,扩展为多按键,修改在定时器运行状态机,在主函数判断,并尽可能多的添加注释方便阅读。。
参考博主文章【源码详解-按键状态机-简洁易懂】1.单个按键实现短按长按的功能(基于STM32)

功能介绍

本程序功能:
使用定时器状态机实现按键单击、双击、长按、连按功能。消抖时间可调,长按时间可调,双击判定时间可调,连按单击间隔可调。无延时不阻塞,稳定触发。移植只需修改读IO函数,结构体初始化和宏定义时间参数即可。

注:

  1. 在定时器状态机判定产生事件标志,在主函数处理并清除事件标志。
  2. 单击、长按、双击事件为抬起时触发。
  3. 使能双击会稍微滞后单击事件的判定,可以选择是否使能双击判定。
  4. 可选择是否使能长按判定。
  5. 连按功能是指长按一定时间不松后,间隔较短时间多次触发快速单击事件,松开后结束。“快速单击”为单独事件,不与单击、长按事件冲突。

代码

头文件 my_key.h

#ifndef ___MY_KEY_H__
#define ___MY_KEY_H__#define KEY_DEBOUNCE_TIME 10      //消抖时间
#define KEY_LONG_PRESS_TIME 500   //长按判定时间
#define KEY_QUICK_CLICK_TIME 100  //连按时间间隔
#define KEY_DOUBLE_CLICK_TIME 200 //双击判定时间#define KEY_PRESSED_LEVEL 0 //按键被按下时的电平
#define TOTAL_KEYS 5        //物理按键数量//按键事件
typedef enum
{KEY_Event_Null,        //空事件KEY_Event_SingleClick, //单击KEY_Event_LongPress,   //长按KEY_Event_QuickClick,  //连击KEY_Event_DoubleClick, //双击
} KEY_EventList_TypeDef;//按键动作
typedef enum
{KEY_Action_Press,   //按住KEY_Action_Release, //松开
} KEY_Action_TypeDef;//按键状态
typedef enum
{KEY_Status_Idle,             //空闲KEY_Status_Debounce,         //消抖KEY_Status_ConfirmPress,     //确认按下KEY_Status_ConfirmPressLong, //确认长按KEY_Status_WaitSecondPress,  //等待再次按下KEY_Status_SecondDebounce,   //再次消抖KEY_Status_SecondPress,      //再次按下
} KEY_StatusList_TypeDef;// 按键引脚的电平
typedef enum
{KKEY_PinLevel_Low,KEY_PinLevel_High,KEY_PinLevel_Unknow,
} KEY_PinLevel_TypeDef;//按键配置
typedef struct
{uint8_t KEY_Label;                 //按键标号uint16_t KEY_Count;                //按键按下计时KEY_Action_TypeDef KEY_Action;     //按键动作,按下或释放KEY_StatusList_TypeDef KEY_Status; //按键状态KEY_EventList_TypeDef KEY_Event;   //按键事件
} KEY_Configure_TypeDef;extern KEY_Configure_TypeDef KeyConfig[TOTAL_KEYS];
extern KEY_EventList_TypeDef key_event[TOTAL_KEYS];
extern uint8_t double_click_mode;
extern uint8_t long_press_mode;void KEY_ReadStateMachine(KEY_Configure_TypeDef *KeyCfg);#endif

源文件 my_key.c

#include "my_key.h"//单独封装函数方便移植
static KEY_PinLevel_TypeDef KEY_ReadPin(uint8_t key_label)
{switch (key_label){case 1:return (KEY_PinLevel_TypeDef)HAL_GPIO_ReadPin(K1_GPIO_Port, K1_Pin);case 2:return (KEY_PinLevel_TypeDef)HAL_GPIO_ReadPin(K2_GPIO_Port, K2_Pin);case 3:return (KEY_PinLevel_TypeDef)HAL_GPIO_ReadPin(K3_GPIO_Port, K3_Pin);case 4:return (KEY_PinLevel_TypeDef)HAL_GPIO_ReadPin(K4_GPIO_Port, K4_Pin);case 5:return (KEY_PinLevel_TypeDef)HAL_GPIO_ReadPin(K5_GPIO_Port, K5_Pin);// case X://   return (KEY_PinLevel_TypeDef)HAL_GPIO_ReadPin(KX_GPIO_Port, KX_Pin);}return KEY_PinLevel_Unknow;
}KEY_Configure_TypeDef KeyConfig[TOTAL_KEYS] = {{1, 0, KEY_Action_Release, KEY_Status_Idle, KEY_Event_Null},{2, 0, KEY_Action_Release, KEY_Status_Idle, KEY_Event_Null},{3, 0, KEY_Action_Release, KEY_Status_Idle, KEY_Event_Null},{4, 0, KEY_Action_Release, KEY_Status_Idle, KEY_Event_Null},{5, 0, KEY_Action_Release, KEY_Status_Idle, KEY_Event_Null},// {X, 0, KEY_Action_Release, KEY_Status_Idle, KEY_Event_Null},
};KEY_EventList_TypeDef key_event[TOTAL_KEYS] = {KEY_Event_Null};
uint8_t double_click_mode = 0; //双击模式
uint8_t long_press_mode = 1;   //长按模式
//按键状态处理
void KEY_ReadStateMachine(KEY_Configure_TypeDef *KeyCfg)
{//按键动作读取if (KEY_ReadPin(KeyCfg->KEY_Label) == KEY_PRESSED_LEVEL){KeyCfg->KEY_Action = KEY_Action_Press;}else{KeyCfg->KEY_Action = KEY_Action_Release;}//状态机switch (KeyCfg->KEY_Status){//状态:空闲case KEY_Status_Idle:if (KeyCfg->KEY_Action == KEY_Action_Press) //动作:按下{KeyCfg->KEY_Status = KEY_Status_Debounce; //状态->消抖KeyCfg->KEY_Event = KEY_Event_Null;       //事件->无}else //动作:默认动作,释放{KeyCfg->KEY_Status = KEY_Status_Idle; //状态->空闲KeyCfg->KEY_Event = KEY_Event_Null;   //事件->无}break;//状态:消抖case KEY_Status_Debounce:if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count >= KEY_DEBOUNCE_TIME)) //动作:消抖时间已到,保持按下{KeyCfg->KEY_Count = 0;                        //计数清零KeyCfg->KEY_Status = KEY_Status_ConfirmPress; //状态->确认按下KeyCfg->KEY_Event = KEY_Event_Null;           //事件->无}else if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count < KEY_DEBOUNCE_TIME)) //动作:时间未到,保持按下{KeyCfg->KEY_Count++;                      //消抖计数KeyCfg->KEY_Status = KEY_Status_Debounce; //状态->维持KeyCfg->KEY_Event = KEY_Event_Null;       //事件->无}else //动作:消抖时间未到,释放,判定为抖动{KeyCfg->KEY_Count = 0;                //计数清零KeyCfg->KEY_Status = KEY_Status_Idle; //状态->空闲KeyCfg->KEY_Event = KEY_Event_Null;   //事件->无}break;//状态:确认按下case KEY_Status_ConfirmPress:if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count >= KEY_LONG_PRESS_TIME)) //动作:长按时间已到,保持按下{KeyCfg->KEY_Count = KEY_QUICK_CLICK_TIME;         //计数置数,生成第一次连按事件KeyCfg->KEY_Status = KEY_Status_ConfirmPressLong; //状态->确认长按KeyCfg->KEY_Event = KEY_Event_Null;               //事件->无}else if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count < KEY_LONG_PRESS_TIME)) //动作:时间未到,保持按下{KeyCfg->KEY_Count++;                          //长按计数KeyCfg->KEY_Status = KEY_Status_ConfirmPress; //状态->维持KeyCfg->KEY_Event = KEY_Event_Null;           //事件->无}else //动作:长按时间未到,释放{if (double_click_mode) //双击模式{KeyCfg->KEY_Count = 0;                           //计数清零KeyCfg->KEY_Status = KEY_Status_WaitSecondPress; //状态->等待再按KeyCfg->KEY_Event = KEY_Event_Null;              //事件->无}else{KeyCfg->KEY_Count = 0;                     //计数清零KeyCfg->KEY_Status = KEY_Status_Idle;      //状态->空闲KeyCfg->KEY_Event = KEY_Event_SingleClick; //事件->单击****}}break;//状态:确认长按case KEY_Status_ConfirmPressLong:if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count >= KEY_QUICK_CLICK_TIME)) //动作:连按时间已到,保持按下{KeyCfg->KEY_Count = 0;                            //计数清零KeyCfg->KEY_Status = KEY_Status_ConfirmPressLong; //状态->维持KeyCfg->KEY_Event = KEY_Event_QuickClick;         //事件->连按****}else if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count < KEY_QUICK_CLICK_TIME)) //动作:时间未到,保持按下{KeyCfg->KEY_Count++;                              //连按计数KeyCfg->KEY_Status = KEY_Status_ConfirmPressLong; //状态->维持KeyCfg->KEY_Event = KEY_Event_Null;               //事件->无}else //动作:长按下后释放{if (long_press_mode) //长按模式{KeyCfg->KEY_Count = 0;                   //计数清零KeyCfg->KEY_Status = KEY_Status_Idle;    //状态->空闲KeyCfg->KEY_Event = KEY_Event_LongPress; //事件->长按****}else //非长按模式{KeyCfg->KEY_Count = 0;                     //计数清零KeyCfg->KEY_Status = KEY_Status_Idle;      //状态->空闲KeyCfg->KEY_Event = KEY_Event_SingleClick; //事件->单击****}}break;//状态:等待是否再次按下case KEY_Status_WaitSecondPress:if ((KeyCfg->KEY_Action != KEY_Action_Press) && (KeyCfg->KEY_Count >= KEY_DOUBLE_CLICK_TIME)) //动作:双击等待时间已到,保持释放{KeyCfg->KEY_Count = 0;                     //计数清零KeyCfg->KEY_Status = KEY_Status_Idle;      //状态->空闲KeyCfg->KEY_Event = KEY_Event_SingleClick; //事件->单击****}else if ((KeyCfg->KEY_Action != KEY_Action_Press) && (KeyCfg->KEY_Count < KEY_DOUBLE_CLICK_TIME)) //动作:时间未到,保持释放{KeyCfg->KEY_Count++;                             //双击等待计数KeyCfg->KEY_Status = KEY_Status_WaitSecondPress; //状态->维持KeyCfg->KEY_Event = KEY_Event_Null;              //事件->无}else //动作:双击等待时间内,再次按下{KeyCfg->KEY_Count = 0;                          //计数清零KeyCfg->KEY_Status = KEY_Status_SecondDebounce; //状态->再次消抖KeyCfg->KEY_Event = KEY_Event_Null;             //事件->无}break;//状态:再次消抖case KEY_Status_SecondDebounce:if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count >= KEY_DEBOUNCE_TIME)) //动作:消抖时间已到,保持按下{KeyCfg->KEY_Count = 0;                       //计数清零KeyCfg->KEY_Status = KEY_Status_SecondPress; //状态->确认再次按下KeyCfg->KEY_Event = KEY_Event_Null;          //事件->无}else if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count < KEY_DEBOUNCE_TIME)) //动作:时间未到,保持按下{KeyCfg->KEY_Count++;                            //消抖计数KeyCfg->KEY_Status = KEY_Status_SecondDebounce; //状态->维持KeyCfg->KEY_Event = KEY_Event_Null;             //事件->无}else //动作:消抖时间未到,释放,判定为抖动{KeyCfg->KEY_Count = 0;                //计数清零KeyCfg->KEY_Status = KEY_Status_Idle; //状态->空闲KeyCfg->KEY_Event = KEY_Event_Null;   //事件->无}break;//状态:再次按下case KEY_Status_SecondPress:if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count >= KEY_LONG_PRESS_TIME)) //动作:长按时间已到,保持按下{if (long_press_mode) //长按模式{KeyCfg->KEY_Count = 0;                            //计数清零KeyCfg->KEY_Status = KEY_Status_ConfirmPressLong; //状态->确认长按KeyCfg->KEY_Event = KEY_Event_SingleClick;        //事件->先响应单击}else //非长按模式{KeyCfg->KEY_Count = 0;                       //计数清零KeyCfg->KEY_Status = KEY_Status_SecondPress; //状态->维持KeyCfg->KEY_Event = KEY_Event_Null;          //事件->无}}else if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count < KEY_LONG_PRESS_TIME)) //动作:长按时间未到,保持按下{KeyCfg->KEY_Count++;                         //计数KeyCfg->KEY_Status = KEY_Status_SecondPress; //状态->维持KeyCfg->KEY_Event = KEY_Event_Null;          //事件->无}else //动作:按下后释放{KeyCfg->KEY_Count = 0;                     //计数清零KeyCfg->KEY_Status = KEY_Status_Idle;      //状态->空闲KeyCfg->KEY_Event = KEY_Event_DoubleClick; //事件->双击}break;}if (KeyCfg->KEY_Event != KEY_Event_Null) //事件记录key_event[KeyCfg->KEY_Label] = KeyCfg->KEY_Event;
}

定时器中断和主函数调用
中断周期为1ms

uint32_t tim_cnt = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if (htim->Instance == htim1.Instance){tim_cnt++;if (tim_cnt % 1 == 0) // 1ms{KEY_ReadStateMachine(&KeyConfig[KEY_1]);KEY_ReadStateMachine(&KeyConfig[KEY_2]);KEY_ReadStateMachine(&KeyConfig[KEY_3]);KEY_ReadStateMachine(&KeyConfig[KEY_4]);KEY_ReadStateMachine(&KeyConfig[KEY_5]);}}
}//调用
int main(void)
{long_press_mode = 1;	 //使能长按模式double_click_mode = 1; //使能双击模式while (1){if (key_event[KEY_1] == KEY_Event_SingleClick) //单击{something1();}if (key_event[KEY_2] == KEY_Event_LongPress) //长按{something2();}if ((key_event[KEY_3] == KEY_Event_QuickClick) || (key_event[KEY_3] == KEY_Event_SingleClick)) //连按{something3();}if (key_event[KEY_4] == KEY_Event_DoubleClick) //双击{something4();}memset(key_event, KEY_Event_Null, sizeof(key_event)); //清除事件}
}

相关文章:

STM32实现按键单击、双击、长按、连按功能,使用状态机,无延时,不阻塞

常见的按键判定程序&#xff0c;如正点原子按键例程&#xff0c;只能判定单击事件&#xff0c;对于双击、长按等的判定逻辑较复杂&#xff0c;且使用main函数循环扫描的方式&#xff0c;容易被阻塞&#xff0c;或按键扫描函数会阻塞其他程序的执行。使用定时器设计状态机可以规…...

C#之Delta并联机械手的视觉同步分拣

本文导读 前面两节课程我们介绍了怎么建立Delta并联机械手的正逆解以及如何通过视觉进行匹配定位。本节课程给大家分享如何通过C#语言开发正运动Delta并联机械手传送带同步的视觉分拣。 VPLC711硬件介绍 VPLC711是正运动推出的一款基于x86平台和Windows操作系统的高性能机器…...

01:Linux的基本命令

Linux的基本命令 1、常识1.1、Linux的隐藏文件1.2、绝对路径与相对路径 2、基本命令2.1、ls2.2、cd2.3、pwd / mkdir / mv / touch / cp / rm / cat / rmdir2.4、ln2.5、man2.6、apt-get 本教程是使用的是Ubuntu14.04版本。 1、常识 1.1、Linux的隐藏文件 在Linux中&#xf…...

GNSS 载波、测距码和导航电文的关系简介

1、GNSS 载波、测距码和导航电文 在卫星导航系统中&#xff0c;载波、测距码和导航电文是构成GPS信号的三个基本组成部分&#xff0c;它们共同工作以实现精确的卫星定位和导航功能。以下是对这三个组成部分的详细介绍&#xff1a; 1. 载波&#xff08;Carrier&#xff09;&…...

deepE 定位系统卡顿问题实战(一) ----------- 锁造成的阻塞问题

deepE介绍 deepE是一个开源的用于端侧(自动驾驶车,机器人)等环境的系统问题与性能分析工具。基于ebpf功能实现 deepE项目地址 欢迎star 测试程序 #include <iostream> #include <thread> #include <mutex>static std::mutex lock;void func1() {int l…...

YOLOv5改进 | 主干网络 | ODConv + ConvNeXt 增强目标特征提取能力

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录&#xff1a; 《YOLOv5入门 …...

TIA博途WinCC通过VB脚本从 Excel中读取数据的具体方法介绍

TIA博途WinCC通过VB脚本从 Excel中读取数据的具体方法介绍 添加 一个PLC,设置PLC的IP地址,如下图所示, 添加全局DB块,新建几个变量,如下图所示, 在数据块中添加了 tag1 …… tag6 ,共 6 个浮点数类型的变量,用来接收通过 WinCC 从 Excel 文件中读取的数据。 添加 HMI…...

第5篇 区块链的技术架构:节点、网络和数据结构

区块链技术听起来很高大上&#xff0c;但其实它的核心架构并不难理解。今天我们就用一些简单的例子和有趣的比喻&#xff0c;来聊聊区块链的技术架构&#xff1a;节点、网络和数据结构。 节点&#xff1a;区块链的“细胞” 想象一下&#xff0c;区块链就像是一个大型的组织&a…...

vue长列表,虚拟滚动

1.新建子组件&#xff0c;将数据传递过去(几万条数据的数组&#xff0c;一次性展示多少条&#xff0c;每条数据的行高). <template><div class"vitualScroll"><sub-scroll :dataList"dataList" :rowCount"20" :rowHeight"2…...

【实战场景】记一次UAT jvm故障排查经历

【实战场景】记一次UAT jvm故障排查经历 开篇词&#xff1a;干货篇&#xff1a;1.查看系统资源使用情况2.将十进制进程号转成十六进制3.使用jstack工具监视进程的垃圾回收情况4.输出指定线程的堆内存信息5.观察日志6.本地环境复现 总结篇&#xff1a;我是杰叔叔&#xff0c;一名…...

线性代数--行列式1

本篇来自对线性代数第一篇的行列式的一个总结。 主要是行列式中有些关键点和注意事项&#xff0c;便于之后的考研复习使用。 首先&#xff0c;对于普通的二阶和三阶行列式&#xff0c;我们可以直接对其进行拆开&#xff0c;展开。 而对于n阶行列式 其行列式的值等于它的任意…...

tensorflow神经网络

训练一个图像识别模型&#xff0c;使用TensorFlow&#xff0c;需要以下步骤。 1. 安装所需的库 首先&#xff0c;确保安装了TensorFlow和其他所需的库。 pip install tensorflow numpy matplotlib2. 数据准备 需要收集和准备训练数据。每个类别应有足够多的样本图像。假设有…...

Python基础001

Python输出语句 print输出字符串 print("中国四大名著&#xff1a;","西游记|","三国演义|","红楼梦|","水浒传") print(6) print(1 1)Python输入语句 input函数 input() input("我的名字是&#xff1a;") p…...

【udp报文】udp报文未自动分片,报文过长被拦截问题定位

问题现象 某局点出现一个奇怪的现象&#xff0c;客户端给服务端发送消息&#xff0c;服务端仅能收到小部分消息&#xff0c;大部分消息从客户端发出后&#xff0c;服务端都未收到。 问题定位 初步分析 根据现象初步分析&#xff0c;有可能是网络原因导致消息到服务端不可达&a…...

某网页gpt的JS逆向

原网页网址 (base64) 在线解码 aHR0cHM6Ly9jbGF1ZGUzLmZyZWUyZ3B0Lnh5ei8 逆向效果图 调用代码&#xff08;复制即用&#xff09; 把倒数第三行换成下面的base64解码 aHR0cHM6Ly9jbGF1ZGUzLmZyZWUyZ3B0Lnh5ei9hcGkvZ2VuZXJhdGU import hashlib import time import reques…...

【python脚本】批量检测sql延时注入

文章目录 前言批量检测sql延时注入工作原理脚本演示 前言 SQL延时注入是一种在Web应用程序中利用SQL注入漏洞的技术&#xff0c;当传统的基于错误信息或数据回显的注入方法不可行时&#xff0c;例如当Web应用进行了安全配置&#xff0c;不显示任何错误信息或敏感数据时&#x…...

在C++中如何理解const关键字的不同用法(如const变量、const成员函数、const对象等)

在C中&#xff0c;const关键字是一个非常重要的修饰符&#xff0c;它用于指明变量、函数参数、成员函数或对象的内容是不可变的。理解const的不同用法对于编写高质量、易维护的C代码至关重要。下面详细解释const在几种不同上下文中的用法和含义。 1. const变量 当变量被声明为…...

JavaSEJava8 时间日期API + 使用心得

文章目录 1. LocalDate2. LocalTime3. LocalDateTime3.1创建 LocalDateTime3.2 LocalDateTime获取方法 4. LocalDateTime转换方法4.1 LocalDateTime增加或者减少时间的方法4.2 LocalDateTime修改方法 5. Period6. Duration7. 格式转换7.1 时间日期转换为字符串7.2 字符串转换为…...

【亲测解决】Python时间问题

微信公众号&#xff1a;leetcode_algos_life&#xff0c;代码随想随记 小红书&#xff1a;412408155 CSDN&#xff1a;https://blog.csdn.net/woai8339?typeblog &#xff0c;代码随想随记 GitHub: https://github.com/riverind 抖音【暂未开始&#xff0c;计划开始】&#xf…...

Linux屏幕驱动开发调试笔记

引言 首先了解下什么是MIPI-DSI&#xff1a; MIPI-DSI是一种应用于显示技术的串行接口&#xff0c;兼容DPI(显示像素接口&#xff0c;Display Pixel Interface)、DBI(显示总线接口&#xff0c;Display Bus Interface)和DCS(显示命令集&#xff0c;Display Command Set)&#…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

基于大模型的 UI 自动化系统

基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...