学习笔记|矩阵按键控制原理|数值转化为键码|密码锁|STC32G单片机视频开发教程(冲哥)|第十四集:矩阵按键原理及实践

news2024/12/23 5:08:56

文章目录

  • 1.矩阵按键是什么
  • 2.矩阵按键的控制原理
  • 3.矩阵按键程序的编写
    • 将数值转化为键码
    • 完整代码:
      • demo.c:
      • key.c:
      • key.h:
  • 密码锁(简易版)
    • 需求分析:
  • 总结
  • 课后练习:

1.矩阵按键是什么

这个矩阵按键也是我们这个开发版上最后一个GPIO的一个应用,如果对1O回的输入跟输出还有什么问题的话,
一定要回过头去看一下我们之前的程序理清楚思路。
之前的按键电路图:
在这里插入图片描述

1个按键占用一个IO口的。
在按键数量较多时,为了减少I/O口的占用,将按键排列成矩阵排列的形式的按键阵列我们称位矩阵按键。

2.矩阵按键的控制原理

电路图:
在这里插入图片描述

按键识别原理:端口默认为高电平,实时读取到引脚为低电平是表示按下。再次读取到高电平,表示松开。

第一步:现将P0.0-P0.3输出低电平,P0.6-P0.7输出高电平,如果有按键按下,按下的那一列的IO就会变成低电平,就可以判断出哪一列按下了。

第二步:现将P0.0-P0.3输出高电平,P0.6-P0.7输出低电平,如果有按键按下,按下的那一行的IO就会变成低电平,就可以判断出哪一行按下了。

第三步:行列组合一下就可以判断出是哪个按键按下了。

按键按下后导线导通,哪条线上有高电平,就会被拉低为低电平,从而检测出是哪条线路,二次检查,交叉节点就是有按键按下的按键位置。

3.矩阵按键程序的编写

先完成矩阵按键的功能编写。
复制9.TIM多任务为10.矩阵按键,用到P0端口,还是在之前的KEY模块基础上进行修改:
在key.h中定义:#define MateixKEY P0 //矩阵按键的引脚
定义函数MateixKEY_Read:
u8 MateixKEY_Read(void); //矩阵按键读取当前是哪一个按钮按下,返回值是按键序号
在key.c中实现函数MateixKEY_Read:
先增加函数头,并把实现思路复制过来作为编写依据,围绕这三步,编写矩阵按键的读取程序:
MateixKEY = 0XC0; //1100 0000 P0.6-P0.7输出高电平,
在这里插入图片描述

增加延时函数MateixKEY_delay留反应时间:
MateixKEY_delay(); //留反应时间
MateixKEY_delay的实现,并添加函数头:

void MateixKEY_delay(void)
{
	u8 i;
	i = 60; //根据之前的毫秒延时函数,可以算出此处延时的时间
	while(--i);
}

第一步:假设P0.7按下, 则为0100 0000,如果想实现哪路按下哪位变成1,可以采用异或运算。
即:0100 0000 ^1100 0000 = 1000 0000
则有:keystate = MateixKEY^0XC0;

第二步:第二次扫描,高位输出低电平,低位输出高电平:MateixKEY = 0X0f; //0000 1111
保存按键状态,假设P0.0按下, 则为0000 1110^0000 1111 = 0000 0001,这里要采用|=,0000 0001 | 1000 0000 = 1000 0001 = 0x81
在这里插入图片描述
keystate |= (MateixKEY^0X0f);是为了避免把之前的数值覆盖。
第三步:keystate中已经保存了行、列的状态,行列组合一下就可以判断出是哪个按键按下了。
printf(“%02x\r\n”,keystate); //强制变为2位,以16进制显示。
return keystate;
在demo.c中调用:
将10ms扫描按键的代码部分注释掉,只检测MateixKEY_Read,加入该函数,编译,运行。按动按键,串口打印对应的16进制数值。

将数值转化为键码

u8 key_val = 0; //表示按键的键码
这里采用switch关键词,直接有模板插入,编写switch函数:

	switch (keystate) //单选开关函数
    {
    	case 0x41:	key_val = 1;
    		break;
    	case 0x42:	key_val = 2;
    		break;
    	case 0x44:	key_val = 3;
    		break;
    	case 0x48:	key_val = 4;
    		break;
    	case 0x81:	key_val = 5;
    		break;
    	case 0x82:	key_val = 6;
    		break;
    	case 0x84:	key_val = 7;
    		break;
    	case 0x88:	key_val = 8;
    		break;
    	default:  	key_val = 0;
    		break;
    }

关闭数码管初始化显示,main函数中新建变量:u8 KEY_NUM = 0; //保存矩阵按键的键码
读取矩阵按键的键码保存在KEY_NUM中,并在数码管最后1位显示:

			KEY_NUM = MateixKEY_Read();
			SEG7 = KEY_NUM; 	//在数码管最后一位显示

编译下载,按动按键就可以判断是哪个按键按下了。

完整代码:

demo.c:

#include "COMM/stc.h"		//调用头文件
#include "COMM/usb.h"
#include "seg_led.h"
#include "key.h"			//调用头文件
#include "beep.h"
#include "tim0.h"



#define MAIN_Fosc 24000000UL	//定义主时钟

char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";

bit TIM_10MS_Flag;		//10ms标志位

void sys_init();	//函数声明
void delay_ms(u16 ms);

void Timer0_Isr(void);


void main()					//程序开始运行的入口
{
	u8 KEY_NUM = 0; 		//保存矩阵按键的键码
	sys_init();				//USB功能+IO口初始化
	usb_init();				//usb库初始化
	Timer0_Init();

	EA = 1;					//CPU开放中断,打开总中断。


	//数码管初始化,显示0-7
//	SEG0 = 0;
//	SEG1 = 1;
//	SEG2 = 2;
//	SEG3 = 3;
//	SEG4 = 4;
//	SEG5 = 5;
//	SEG6 = 6;
//	SEG7 = 7;

	LED = 0x0f;	//赋初值,亮一半灭一半,可以写8位的变量.从7开始数到0

	while(1)		//死循环
	{
//		if( DeviceState != DEVSTATE_CONFIGURED ) 	//
//			continue;
		if( bUsbOutReady )
		{
			usb_OUT_done();
		}
		if(TIM_10MS_Flag == 1)   //将需要延时的代码部分放入
		{
			TIM_10MS_Flag = 0;		//TIM_10MS_Flag 变量清空置位
//			KEY_Deal();				//P3上所有端口都需要执行一遍
			BEEP_RUN();				//蜂鸣运行

//			if(KEY_ReadState(KEY1)== KEY_RESS)	//判断KEY1按钮是否为单击
//			{
//				BEEP_ON(2);							//蜂鸣20ms
//				LED0 = 0;
//			}
//			else if(KEY_ReadState(KEY1)== KEY_LONGPRESS) //判断KEY1按钮是否为长按
//			{
//				BEEP_ON(2);							//蜂鸣20ms
//				LED1 = 0;
//			}
//			else if(KEY_ReadState(KEY1)== KEY_RELAX)	//判断KEY1按钮是否为松开
//			{
//				LED = 0XFF;
//			}
			KEY_NUM = MateixKEY_Read();
			SEG7 = KEY_NUM; 	//在数码管最后一位显示
		}

	}
}

void sys_init()		//函数定义
{
    WTST = 0;  //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
    EAXFR = 1; //扩展寄存器(XFR)访问使能
    CKCON = 0; //提高访问XRAM速度

	P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
    P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
    P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
    P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
    P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
    P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
    P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
    P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口

    P3M0 = 0x00;
    P3M1 = 0x00;

    P3M0 &= ~0x03;
    P3M1 |= 0x03;

    //设置USB使用的时钟源
    IRC48MCR = 0x80;    //使能内部48M高速IRC
    while (!(IRC48MCR & 0x01));  //等待时钟稳定

    USBCLK = 0x00;	//使用CDC功能需要使用这两行,HID功能禁用这两行。
    USBCON = 0x90;
}


void delay_ms(u16 ms)	//unsigned int
{
	u16 i;
	do
	{
		i = MAIN_Fosc/6000;
		while(--i);
	}while(--ms);
}

void Timer0_Isr(void) interrupt 1 //1ms进来执行一次,无需其他延时,重复赋值
{
	static timecount = 0;

	SEG_LED_Show();		//数码管刷新

	timecount++;		//1ms+1
	if(timecount>=10)	//如果这个变量大于等于10,说明10ms到达
	{
		timecount = 0;
		TIM_10MS_Flag = 1;	//10ms到了
	}
}

key.c:

#include "key.h"			//调用头文件
u16 Count[8] = {0,0,0,0,0,0,0,0};	//按键的时间状态变量初始化8位
u8 LastState = 0;					//8位变量,b0=1 则表示key0上一次按下过


//========================================================================
// 函数名称:KEY_Deal
// 函数功能:按键状态的获取
// 入口参数:无
// 函数返回:无
// 当前版本: VER1.0
// 修改日期: 2023-1-1
// 当前作者:
// 其他备注:循环读取8个端口的状态,并将按下的时间赋值给Count数组,然后按下的状态赋值给变量LastState
//========================================================================
void KEY_Deal(void) 			//检查所有的按键状态,10ms执行一次
{
	u8 i = 0;
	for(i=0;i<8;i++)			//for循环变量 循环8次,i取值为0-7,代表P30-P37的状态查询
	{
		if(~KEY & (1<<i) )					//如果持续按下,变量加1
		{
			if(Count[i] < 60000)			// Count是u16类型,最大值小于65535,故增加限定条件。
			Count[i] ++;					//如果持续按下,这个变量加1
		}
		else					//如果按键松开,变量清0
		{
			if(Count[i] > 0 )	//如果这个按键是按下过的,
			{
				LastState |= (1<<i);	//这个变量相应的标志位置1,单独写对应位
			}
			else
			{
				LastState &= ~(1<<i);	//仅操作这个变量相应的标志位清0
			}
			Count[i] = 0;					//如果松开,计数变量清0
		}
	}

}
//========================================================================
// 函数名称:KEY_ReadState
// 函数功能:读取指定的按钮的状态
// 入口参数: @keynum; 按钮的端口号,例如P32的端口号是2,
// 函数返回:见KEY的返回值的各种状态,看其他备注
// 当前版本: VER1.0
// 修改日期: 2023-1-1
// 当前作者:
// 其他备注: 函数返回值如下:
状态	功能
//#define KEY_NOPRESS 0 	//按键未按下	0
//#define KEY_FLCKER 1 		//消抖	1
//#define KEY_RESS 2		//单击	2
//#define KEY_PRESSOVER 3 	//单击结束	3
//#define KEY_LONGPRESS 4 	//长按3s	4
//#define KEY_LONGOVER 5 	//长按结束	5
//#define KEY_RELAX 6 		//按键松开	6
//========================================================================

u8  KEY_ReadState(u8 keynum) 	//读取指定按键的状态,10ms执行1次
{
	if(Count[keynum] > 0)			//判断按键是按下的
	{
		if(Count[keynum] < 3)		//按下小于30ms,返回消抖状态
		{
			return KEY_FLCKER;
		}
		else if(Count[keynum] == 3)	//按正好等于30ms,返回单击状态
		{
			return KEY_RESS;
		}
		else if(Count[keynum] < 300 ) //按下小于3000ms,返回单击结束
		{
			return KEY_PRESSOVER;
		}
		else if(Count[keynum] == 300 ) //按下正好等于3000ms,返回长按
		{
			return KEY_LONGPRESS;
		}
		else					//长按结束
		{
			return KEY_LONGOVER;
		}
	}
	else						//按键已经松开了,返回KEY_RELAX状态
	{
		if(LastState &(1<<keynum))			//按键之前按下过,要判断上1s是不是高电平,如果上1s是低电平,说明是按键按下
											//例如,要判断P32,P30,31,32,左移2位,
											//按键已经松开了
		{
			return KEY_RELAX;
		}
		else			//按键之前没有按下过,返回未按下
		{
			return KEY_NOPRESS;
		}
	}
}
//========================================================================
// 函数名称:MateixKEY_delay
// 函数功能:矩阵按键读取专用延时函数
// 入口参数:无
// 函数返回:无
// 当前版本: VER1.0
// 修改日期: 2023
// 当前作者:
// 其他备注:
//========================================================================
void MateixKEY_delay(void)
{
	u8 i;
	i = 60; //根据之前的ms延时的函数,可以算出此处延时的时间
	while(--i);
}
//========================================================================
// 函数名称:MateixKEY_Read
// 函数功能:矩阵按键读取当前是哪一个按钮按下
// 入口参数:无
// 函数返回:0表示没有按键按下,1-8表示当前是第几个按键按下
// 当前版本: VER1.0
// 修改日期: 2023-1-1
// 当前作者:
// 其他备注:
//========================================================================
u8 MateixKEY_Read(void) 	//矩阵按键读取当前是哪一个按钮按下,返回值是按键序号
{
	u8 keystate;				//表示当前的按钮状态值
	u8 key_val = 0;				//表示按键的键码
//	第一步:现将P0.0-P0.3输出低电平,P0.6-P0.7输出高电平,如果有按键按下,按下的那一列的IO就会变成低电平,就可以判断出哪一列按下了。
	MateixKEY = 0XC0;			//1100 0000  P0.6-P0.7输出高电平,
	MateixKEY_delay();			//留反应时间
	keystate = (MateixKEY^0XC0);	//保存按键状态,假设P0.7按下, 则为0100 0000,如果想实现变成哪路按下变成1,用异或

//	第二步:现将P0.0-P0.3输出高电平,P0.6-P0.7输出低电平,如果有按键按下,按下的那一行的IO就会变成低电平,就可以判断出哪一行按下了。
	MateixKEY = 0X0f;			//0000 1111  高位输出低电平,低位输出高电平
	MateixKEY_delay();			//留反应时间
	keystate |= (MateixKEY^0X0f);	//保存按键状态,假设P0.0按下, 则为0000 1110^0000 1111 = 0000 0001,这里要采用|=,为了避免把之前的数值覆盖。

//	第三步:行列组合一下就可以判断出是哪个按键按下了。
//	printf("%02x\r\n",keystate); //强制变为2位,以16进制显示。
	switch (keystate) //单选开关函数
    {
    	case 0x41:	key_val = 1;
    		break;
    	case 0x42:	key_val = 2;
    		break;
    	case 0x44:	key_val = 3;
    		break;
    	case 0x48:	key_val = 4;
    		break;
    	case 0x81:	key_val = 5;
    		break;
    	case 0x82:	key_val = 6;
    		break;
    	case 0x84:	key_val = 7;
    		break;
    	case 0x88:	key_val = 8;
    		break;
    	default:  	key_val = 0;
    		break;
    }

	return key_val;

}

key.h:

#ifndef __KEY_H
#define __KEY_H

#include "COMM/stc.h"			//调用头文件
#include "COMM/usb.h"

//------------------------引脚定义------------------------//
#define KEY P3 			//定义一个按键 引脚选择P32-P36

#define KEY1 2			//按键1
#define KEY2 3			//按键2
#define KEY3 4			//按键3
#define KEY4 5			//按键4

#define MateixKEY P0    //矩阵按键的引脚


//------------------------变量声明------------------------//
//状态	功能
#define KEY_NOPRESS 0 		//按键未按下	0
#define KEY_FLCKER 1 		//消抖	1
#define KEY_RESS 2			//单击	2
#define KEY_PRESSOVER 3 	//单击结束	3
#define KEY_LONGPRESS 4 	//长按3s	4
#define KEY_LONGOVER 5 		//长按结束	5
#define KEY_RELAX 6 		//按键松开	6


//------------------------函数声明-----------------------//
void KEY_Deal(void);			//检查所有的按键状态
u8  KEY_ReadState(u8 keynum);	//读取指定按键的状态

u8 MateixKEY_Read(void);		//矩阵按键读取当前是哪一个按钮按下,返回值是按键序号

#endif

密码锁(简易版)

在这里插入图片描述

由于KEY_NUM = MateixKEY_Read();执行后,会持续输出1,需要修改,3s内只输出1次即可,让它符合今天的主题。
先定义一个静态变量,这是一个很常见的用法,static u8 keystate_Last; //表示当前的按钮上一次的状态值
增加判断条件:

	if(keystate_Last != keystate)	//如果本次获取到的按键状态值和之前的不一样
	{
		keystate_Last = keystate;   //把本次的按键状态值写入进去

		switch (keystate) //单选开关函数
		{
			case 0x41:	key_val = 1;
				break;
			case 0x42:	key_val = 2;
				break;
			case 0x44:	key_val = 3;
				break;
			case 0x48:	key_val = 4;
				break;
			case 0x81:	key_val = 5;
				break;
			case 0x82:	key_val = 6;
				break;
			case 0x84:	key_val = 7;
				break;
			case 0x88:	key_val = 8;
				break;
			default:  	key_val = 0;
				break;
		}
		printf("%d\r\n",(int)key_val);  //强制转化为整形变量
	}

需求分析:

1.通过LED0模拟门锁状态,LED点亮表示门锁打开,熄灭表示门锁锁上;
增加横线显示值,SEG_Tab[22]=
默认显示横线:u8 Show_Tab[8] = {21,21,21,21,21,21,21,21};
新增变量:u8 KEY_Str = 0; //表示当前输入了几个密码
2.增加8位数码管,可以动态显示8位的密码,无密码时显示 “- - - - - - - -”;
3.通过矩阵按键可以输入1-8的数字表示密码,并依次显示在数码管上;
4.每输入一个数字,蜂鸣器响20ms表示有数字按下;
5.密码正确打开LED0,密码错误蜂鸣响2秒;
根据条件,修改demo.c中的main函数代码如下:

void main()					//程序开始运行的入口
{
	u8 KEY_NUM = 0; 		//保存矩阵按键的键码
	u8 KEY_Str = 0; 		//表示当前输入了几个密码位
	sys_init();				//USB功能+IO口初始化
	usb_init();				//usb库初始化
	Timer0_Init();

	EA = 1;					//CPU开放中断,打开总中断。


	//数码管初始化,显示0-7
//	SEG0 = 0;
//	SEG1 = 1;
//	SEG2 = 2;
//	SEG3 = 3;
//	SEG4 = 4;
//	SEG5 = 5;
//	SEG6 = 6;
//	SEG7 = 7;

	//LED = 0x0f;	//赋初值,亮一半灭一半,可以写8位的变量.从7开始数到0
	LED = 0xff;	//赋初值,密码锁应用初始状态熄灭所有LED
	while(1)		//死循环
	{
//		if( DeviceState != DEVSTATE_CONFIGURED ) 	//
//			continue;
		if( bUsbOutReady )
		{
			usb_OUT_done();
		}
		if(TIM_10MS_Flag == 1)   //将需要延时的代码部分放入
		{
			TIM_10MS_Flag = 0;		//TIM_10MS_Flag 变量清空置位
//			KEY_Deal();				//P3上所有端口都需要执行一遍
			BEEP_RUN();				//蜂鸣运行

//			if(KEY_ReadState(KEY1)== KEY_RESS)	//判断KEY1按钮是否为单击
//			{
//				BEEP_ON(2);							//蜂鸣20ms
//				LED0 = 0;
//			}
//			else if(KEY_ReadState(KEY1)== KEY_LONGPRESS) //判断KEY1按钮是否为长按
//			{
//				BEEP_ON(2);							//蜂鸣20ms
//				LED1 = 0;
//			}
//			else if(KEY_ReadState(KEY1)== KEY_RELAX)	//判断KEY1按钮是否为松开
//			{
//				LED = 0XFF;
//			}
			KEY_NUM = MateixKEY_Read();		//当前矩阵按键的键值
			//SEG7 = KEY_NUM; 	//在数码管最后一位显示
			if( KEY_NUM > 0)				//如果有按键按下
			{
				KEY_NUM = 0;						//键值先清空,清空按键
				BEEP_ON(2);							//蜂鸣20ms
				Show_Tab[KEY_Str] = KEY_NUM; 		//表示当前输入了几个密码 = KEY_NUM;		//将当前的按键状态保存到数组
				KEY_Str++;							//输入的密码位数+1

				if(KEY_Str ==8)		//如果密码已经等于8位,
				{
					if((Show_Tab[0]==1)&&(Show_Tab[1]==1)&&(Show_Tab[2]==1)&&(Show_Tab[3]==1)&&(Show_Tab[4]==1)&&(Show_Tab[5]==1)&&(Show_Tab[6]==1)&&(Show_Tab[7]==1))
					{
						LED0 = 0;			//如果密码正确,LED0点亮
					}
					else
					{
						BEEP_ON(200);	//密码错误,蜂鸣2s。单位是10ms,2000ms=2s
					}
				}
			}
		}

	}
}

编译下载,发现按键后均显示0:KEY_NUM = 0;位置有误,应该放在调用以后再置0.
重新测试,8位输入后不能自动清空,应该添加代码,到达长度后回到初始值显示横杠:SEG0 = SEG1 = SEG2 = SEG3 = SEG4 = SEG5 = SEG6 = SEG7 = 21;
重新测试,输入错误后,复位,再按键输入,显示有问题:KEY_Str ==8后忘记归0了。
经过修改,功能已正常实现。
完整代码请参考:《STC单片机原理-教学视频配套附件-20230731.zip

总结

1.了解矩阵按键的工作原理和代码编写的过程

课后练习:

给今天的门锁增加如下功能:
1.LED0(门锁)打开后,5秒后自动关闭;
2.增加门内的手动开门按钮,按下按钮门锁打开;
3.10秒内没有输入密码自动数码管熄灭省电;有按键按下时再显示。
4.用for去改写一下密码判断的地方。

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

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

相关文章

网上管理系统的分析及设计---应用UML建模

目 录 第1章 系统需求 第2章 需求分析 2.1 识别参与者 2.2 识别用例 2.3 用例的事件流描述 第3章 静态结构模型 3.1 定义系统对象 3.2 定义用户界面类 3.3 建立类图 第4章 动态行为模型 4.1 创建系统顺序图&#xff08;协作图&#xff09; 4.2 创建系统…

网络爬虫-----初识爬虫

目录 1. 什么是爬虫&#xff1f; 1.1 初识网络爬虫 1.1.1 百度新闻案例说明 1.1.2 网站排名&#xff08;访问权重pv&#xff09; 2. 爬虫的领域&#xff08;为什么学习爬虫 ?&#xff09; 2.1 数据的来源 2.2 爬虫等于黑客吗&#xff1f; 2.3 大数据和爬虫又有啥关系&…

Java低代码:jvs-list (子列表)表单回显及触发逻辑引擎配置说明

一、子列表【新增】表单默认回显主列表关联字段 子列表新增表单可使用表单回显配置&#xff0c;在新增表单中默认回显&#xff0c;如图效果 1、子列表中进入新增页面配置 2、切换到表单设置&#xff0c;选择回显设置&#xff0c;进入回显逻辑引擎。 3、在画布中拖入【对象变量…

记录一次对登录接口的性能测试

测试环境 客户端: win10 这里可以用linux,但没用,因为想直观查看结果。 被测环境:linux X86 4核CPU16G内存 被测接口:登录接口,没有做数据驱动。 场景设计 设置线程数19,持续时间5分钟,并用后端监听器监听结果,使用grafana+prometheus监控服务器资源。 测试执行 …

fabic如何将绘图原点移到画布中心

情况说明&#xff1a; fabic默认绘图原点为left&#xff1a;0&#xff0c;top&#xff1a;0 后端给我的内容是按照x&#xff0c;y返回的&#xff0c;需要将坐标系移到fabic画布的中心位置&#xff0c;找了下网上合适的砖&#xff0c;想一句命令直接设置&#xff0c;结果没有。…

二叉排序树(BST)的算法分析以及基本操作(结点的查询,插入,删除)

1.二叉排序树的定义 二叉排序树&#xff0c;又称二叉查找树&#xff08;BST&#xff0c;Binary Search Tree) 默认不允许两个结点的关键字相同。 1.二叉排序树的性质: 任意一棵二叉排序树的子树的结点大小都满足“左小右大”。 左子树上所有结点的关键字均小于根结点的关键…

廉价的全闪存雷电 NAS 折腾笔记:NUC9 操作系统踩坑

上一篇文章中&#xff0c;分享了关于低成本全闪存 NAS 的个人方案选择。 本篇文章&#xff0c;来聊聊硬件相关部分&#xff0c;以及软件的基础配置部分&#xff0c;也聊聊雷电组网的踩坑之旅。 写在前面 我使用的设备是 NUC9i5QNX&#xff0c;这台设备的硬件基础规格&#x…

【软件测试】selenium3

自动化测试的概念 自动化测试指软件测试的自动化&#xff0c;在预设状态下运行应用程序或者系统&#xff0c;预设条件包括正常和异常&#xff0c;最 后评估运行结果。将人为驱动的测试行为转化为机器执行的过程。 自动化测试就相当于将人工测试手段进行转换&#xff0c;让代码…

国产视觉检测设备崛起,以AI机器视觉及自研算法破解智造难题

机器视觉作为人工智能的前沿分支之一&#xff0c;被称为智能制造的“智慧之眼”&#xff0c;在工业领域中&#xff0c;能够代替人工完成识别、测量、定位、检测等工作&#xff0c;以实现对设备精密控制及产线智能化、自动化升级。 同时&#xff0c;深度学习和3D视觉的技术升级…

Java本地开发环境搭建

概述 Java语言是企业级应用软件开发语言&#xff0c;本文主要描述Java开发环境的搭建。 如上所示&#xff0c;TIOBE提供2023年9月份全球开发语言的排行榜&#xff0c;其中&#xff0c;Java排名第四&#xff0c;而Python已经跃升到第一位&#xff0c;因为&#xff0c;Python是人…

芯科蓝牙BG27开发笔记6-精简第一个程序

1. 这些IO的控制代码在哪里&#xff1f; 还是蓝牙点灯程序&#xff1a; 首先需要对pinout做一些精简&#xff1a; 为了简化工程&#xff0c;去掉了不必要的IO。 至于PTI接口是什么&#xff0c;怎么用&#xff0c;不知道&#xff0c;现在不考虑&#xff1a; 但是提出以下问题…

第6章_freeRTOS入门与工程实践之创建FreeRTOS工程

本教程基于韦东山百问网出的 DShanMCU-F103开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id724601559592 配套资料获取&#xff1a;https://rtos.100ask.net/zh/freeRTOS/DShanMCU-F103 freeRTOS系列教程之freeRTOS入…

应该下那个 ActiveMQ

最近在搞 ActiveMQ 的时候&#xff0c;发现有 2 个 ActiveMQ 可以下载。 应该下那个呢&#xff1f; JMS 即Java Message Service&#xff0c;是JavaEE的消息服务接口。 JMS主要有两个版本&#xff1a;1.1和2.0。 2.0和1.1相比&#xff0c;主要是简化了收发消息的代码。 所谓…

Redis——其他数据类型介绍

概要介绍 Redis中有10种不同的数据类型。之前的blog中介绍了Redis中常见的五大数据类型&#xff1a;String&#xff0c;List&#xff0c;Hash&#xff0c;Set&#xff0c;ZSet。而Redis中还有许多其他的数据类型&#xff0c;一般在特定的场景中使用 Stream 首先介绍一下什么…

笔记本多拓展出一个屏幕

一、首先要知道&#xff0c;自己的电脑有没有Type-c接口&#xff0c;支持不支持VGA 推荐&#xff1a; 自己不清楚&#xff0c;问客服&#xff0c;勤问。 二、显示屏与笔记本相连&#xff0c;通过VGA 三、连接好了&#xff0c;需要去配置 网址&#xff1a;凑合着看&#xff…

代码随想录算法训练营Day42 | 动态规划(4/17) 0-1背包问题理论基础 LeetCode 416.分割等和子集

开始背包问题的练习&#xff01; 1. 背包问题的理论基础 对于面试的话&#xff0c;其实掌握01背包&#xff0c;和完全背包&#xff0c;就够用了&#xff0c;最多可以再来一个多重背包。这里附上代码随想录的图&#xff0c;可以对背包问题进行一个分类。 1.1 十分重要的基础&a…

Java class 文件安全加密工具对比与ClassFinal实战

文章目录 前言常见加密方案对比XJarProGuardClassFinal ClassFinal实战纯命令方式maven插件方式 写在最后 前言 相信不少的同学开发的软件都是用户商业化&#xff0c;对于这些商业运营的项目很多都会直接部署在客户方&#xff0c;这样就可能会导致项目源码泄露。当然&#xff…

每日一题~中序后序遍历构造二叉树

题目描述&#xff1a; 思路分析&#xff1a; 后序遍历分析图 中序遍历分析图 不难看出后序遍历的结果中的最后一个元素就是根节点&#xff0c;倒数第二个元素则是根节点的右子树的根节点&#xff0c;而倒数第三个元素是右子树的新右子树的根节点&#xff0c;依次类推。我们可…

部署ik分词器

部署ik分词器 案例版本&#xff1a;elasticsearch-analysis-ik-8.6.2 ​ ES默认自带的分词器对中文处理不够友好&#xff0c;创建倒排索引时可能达不到我们想要的结果&#xff0c;然而IK分词器能够很好的支持中文分词 ​ 因为是集群部署&#xff0c;所以每台服务器中的ES都需…

springboot redisTemplate.opsForValue().setIfAbsent返回null原理

一、版本 springboot版本&#xff1a;spring-boot-starter-data-redis 2.1.6 redisson版本&#xff1a;redisson-spring-boot-starter 3.11.5 二、场景 Boolean res redisTemplate.opsForValue().setIfAbsent("key","value");以上代码同一时间多次执行…