Linux基础项目开发day2:量产工具——输入系统

文章目录

  • 前言
  • 一、数据结构抽象
    • 1、数据本身
    • 2、设备本身
    • 3、input_manager.h
  • 二、触摸屏编程
    • 1、touchscreen.c
  • 三、触摸屏单元测试
    • 1、touchscreen.c
    • 2、上机测试
  • 四、网络编程
    • netiput.c
  • 五、网络单元测试
    • 1、netiput.c
    • 2、client.c
    • 3、上机测试
  • 六、输入系统的框架
    • 1、框架思路
    • 2、input_,manager.c
    • 3、环形缓冲区
    • 4、input_,manager.c加入环形Buffer操作函数
  • 七、输入管理单元测试
    • 1、input_test.c
    • 2、input下的Makefile
    • 3、uinttest下的Makefile
    • 4、上板测试

前言

前面我们实现了显示系统,现在我们来学习输入系统,类似的,都需要我们来进行学习搭建!具体分为三层:
在这里插入图片描述

一、数据结构抽象

对于每一个设备,每一个模块,都用结构体来比表示他,以后就可以很方便的去替换这些模块,所以我们需要抽象出两个结构体,分别是:1、数据本身,2、设备本身。
在这里插入图片描述
输入来源:1、网络数据输入 2、点击时间输入(触摸屏)
在这里插入图片描述

1、数据本身

在这里插入图片描述

iType——判断是什么数据
INPUT_TYPE_TOUCH——触摸屏事件
INPUT_TYPE_NET——网络输入事件
触摸屏数据
int iX;、int iY: 判断具体坐标
int iPressure: 压力值
网络数据
char str[1024]: 网络数据

2、设备本身

在这里插入图片描述

*name: 设备名字
(*GetInputEvent)(PInputEvent ptInputEvent):上层代码通过这个函数获得数据,返回值判断是否成功,如果成功结果保存至PInputEvent ptInputEvent中
(*DeviceInit)(void):提供初始化函数,比如打开设备节点等
(*DeviceExit)(void):提供退出函数
InputDevice *ptNext;链表所需要

3、input_manager.h

#ifndef _INPUT_MANAGER_H
#define _INPUT_MANAGER_H#include <sys/time.h>#define INPUT_TYPE_TOUCH 1
#define INPUT_TYPE_NET   2typedef struct InputEvent {struct timeval	tTime;int iType;int iX;int iY;int iPressure;char str[1024];
}InputEvent, *PInputEvent;typedef struct InputDevice {char *name;int (*GetInputEvent)(PInputEvent ptInputEvent);int (*DeviceInit)(void);int (*DeviceExit)(void);struct InputDevice *ptNext;
}InputDevice, *PInputDevice;#endif

二、触摸屏编程

1、touchscreen.c

#include <input_manager.h>
#include <tslib.h>//定义一个指向触摸屏设备的静态全局变量
static struct tsdev *g_ts;//用于从触摸屏设备获取输入事件
static int TouchscreenGetInputEvent(PInputEvent ptInputEvent)
{struct ts_sample samp;int ret;//从触摸屏读取一个样品ret = ts_read(g_ts, &samp, 1);if (ret != 1)return -1;//填充数据,便于保存ptInputEvent->iType     = INPUT_TYPE_TOUCH;ptInputEvent->iX        = samp.x;ptInputEvent->iY        = samp.y;ptInputEvent->iPressure = samp.pressure;ptInputEvent->tTime     = samp.tv;//读取成功,返回0return 0;
}//用于初始化触摸屏设备
static int TouchscreenDeviceInit(void)
{//初始化触摸屏设备g_ts = ts_setup(NULL, 0);if (!g_ts){printf("ts_setup err\n");return -1;}return 0;
}//用于退出触摸屏设备
static int TouchscreenDeviceExit(void)
{//关闭触摸屏设备ts_close(g_ts);return 0;
}//定义一个触摸屏实例
static InputDevice g_tTouchscreenDev ={.name = "touchscreen",.GetInputEvent  = TouchscreenGetInputEvent,.DeviceInit     = TouchscreenDeviceInit,.DeviceExit     = TouchscreenDeviceExit,
};

三、触摸屏单元测试

直接在touchscreen.c添加一个主函数进行测试!

1、touchscreen.c

#if 1int main(int argc, char **argv)
{InputEvent event;int ret;g_tTouchscreenDev.DeviceInit();while (1){ret = g_tTouchscreenDev.GetInputEvent(&event);if (ret) {printf("GetInputEvent err!\n");return -1;}else{printf("Type      : %d\n", event.iType);printf("iX        : %d\n", event.iX);printf("iY        : %d\n", event.iY);printf("iPressure : %d\n", event.iPressure);}}return 0;
}#endif
EXTRA_CFLAGS  := 
CFLAGS_file.o := #obj-y += disp_test.o

将unittest文件夹中的Makefile中的main函数部分注释掉,因为一个程序只能允许有一个main

EXTRA_CFLAGS  := 
CFLAGS_file.o := obj-y += touchscreen.o

input目录下的Makefile

 
CROSS_COMPILE ?= 
AS		= $(CROSS_COMPILE)as
LD		= $(CROSS_COMPILE)ld
CC		= $(CROSS_COMPILE)gcc
CPP		= $(CC) -E
AR		= $(CROSS_COMPILE)ar
NM		= $(CROSS_COMPILE)nmSTRIP		= $(CROSS_COMPILE)strip
OBJCOPY		= $(CROSS_COMPILE)objcopy
OBJDUMP		= $(CROSS_COMPILE)objdumpexport AS LD CC CPP AR NM
export STRIP OBJCOPY OBJDUMPCFLAGS := -Wall -O2 -g
CFLAGS += -I $(shell pwd)/includeLDFLAGS := -ltsexport CFLAGS LDFLAGSTOPDIR := $(shell pwd)
export TOPDIRTARGET := testobj-y += display/
obj-y += input/all : start_recursive_build $(TARGET)@echo $(TARGET) has been built!start_recursive_build:make -C ./ -f $(TOPDIR)/Makefile.build$(TARGET) : built-in.o$(CC) -o $(TARGET) built-in.o $(LDFLAGS)clean:rm -f $(shell find -name "*.o")rm -f $(TARGET)distclean:rm -f $(shell find -name "*.o")rm -f $(shell find -name "*.d")rm -f $(TARGET)

需要修改顶层目录下的Makefile
第20行:LDFLAGS := -lts 设置链接
第31行:打开input目录下的文件

2、上机测试

上机效果:
在这里插入图片描述

四、网络编程

对网络输入构造出同触摸屏编程中一样的结构体!
在这里插入图片描述

netiput.c

关于网络编程API的学习也可以看看我写的一个文章:网络编程(TCP&UDP)

#include <input_manager.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>/* socket* bind* sendto/recvfrom*//*  定义服务端口号 */
#define SERVER_PORT 8888/*  用于储存套接字描述符 */
static int g_iSocketServer;/*  用于从网络套接字获取输入事件 */
static int NetinputGetInputEvent(PInputEvent ptInputEvent)
{struct sockaddr_in tSocketClientAddr;int iRecvLen;unsigned char ucRecvBuf[1000];int iAddrLen = sizeof(struct sockaddr);/* 获取数据 */iRecvLen = recvfrom(g_iSocketServer, ucRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);if (iRecvLen > 0){ucRecvBuf[iRecvLen] = '\0';//在接受缓冲区尾端添加字符串结束符//填充输入事件结构体ptInputEvent->iType 	= INPUT_TYPE_NET;gettimeofday(&ptInputEvent->tTime, NULL);strncpy(ptInputEvent->str, ucRecvBuf, 1000);ptInputEvent->str[999] = '\0';return 0;}elsereturn -1;
}/*  用于初始网络输入设备 */
static int NetinputDeviceInit(void)
{struct sockaddr_in tSocketServerAddr;//服务地址结构体int iRet;//返回值int iClientNum = -1;/* 创建一个UDP套接字 */g_iSocketServer = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == g_iSocketServer)//创建失败{printf("socket error!\n");return -1;}//设置服务地址结构体tSocketServerAddr.sin_family      = AF_INET;tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* host to net, short */tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;memset(tSocketServerAddr.sin_zero, 0, 8);//绑定套接字到服务器地址iRet = bind(g_iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));if (-1 == iRet){printf("bind error!\n");return -1;}return 0;
}/* 用于退出网络输入设备 */
static int NetinputDeviceExit(void)
{close(g_iSocketServer);	return 0;
}/* 定义一个结构体实例 */
static InputDevice g_tNetinputDev ={.name = "touchscreen",.GetInputEvent  = NetinputGetInputEvent,.DeviceInit     = NetinputDeviceInit,.DeviceExit     = NetinputDeviceExit,
};/* 注册网络输入设备到输入管理器 */
void NetInputRegister(void)
{RegisterInputDevice(&g_tNetinputDev);/*void RegisterInputDevice(PInputDevice ptInputDev)*{*	 ptInputDev->ptNext = g_InputDevs;*	 g_InputDevs = ptInputDev;*}*/}

五、网络单元测试

1、netiput.c

这里直接在netiput.c里面添加测试!这里充当服务端

#if 1int main(int argc, char **argv)
{InputEvent event;int ret;g_tNetinputDev.DeviceInit();while (1){ret = g_tNetinputDev.GetInputEvent(&event);if (ret) {printf("GetInputEvent err!\n");return -1;}else{printf("Type      : %d\n", event.iType);printf("str       : %s\n", event.str);}}return 0;
}#endif

注意:需要把触摸屏里面的main函数注释掉!!!
Makefile:

EXTRA_CFLAGS  := 
CFLAGS_file.o := obj-y += touchscreen.o
obj-y += netinput.o

2、client.c

这里充当客户端,用于发送数据

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>/* socket* connect* send/recv*/#define SERVER_PORT 8888int main(int argc, char **argv)
{int iSocketClient;struct sockaddr_in tSocketServerAddr;int iRet;int iSendLen;int iAddrLen;if (argc != 3){printf("Usage:\n");printf("%s <server_ip> <str>\n", argv[0]);return -1;}iSocketClient = socket(AF_INET, SOCK_DGRAM, 0);tSocketServerAddr.sin_family      = AF_INET;tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* host to net, short *///tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr)){printf("invalid server_ip\n");return -1;}memset(tSocketServerAddr.sin_zero, 0, 8);#if 0iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));	if (-1 == iRet){printf("connect error!\n");return -1;}
#endifiAddrLen = sizeof(struct sockaddr);iSendLen = sendto(iSocketClient, argv[2], strlen(argv[2]), 0,(const struct sockaddr *)&tSocketServerAddr, iAddrLen);close(iSocketClient);return 0;
}

3、上机测试

分别编译好后复制到板子上
在这里插入图片描述

六、输入系统的框架

1、框架思路

在这里插入图片描述

1、 输入管理器inputmanager实现函数:
InputInit
InputDeviceInit
GetInputEvent
2、输入管理器支持多个设备,那怎么将他们连起来呢?
InputInit函数创建一个链表,链表指向网络输入设备本身和触摸屏设备本身,通过链表可以找到所有的输入设备!
3.对于每一个设备都可以调用里面的 GetInputEvent 来获得数据,如果想同时获得多个设备的输入数据的话,那么该怎么获得?
调用InputDeviceInit从链表里面把每一个设备取出来,调用里面的DeviceInit 初始化,并且为每一个输入设备创造一个线程thread,线程不断调用设备里面的GetInputEvent 等待数据,一旦得到数据,就可以将获得的数据放到某个buffer
4.上层的应用程序调用GetInputEvent 时候就会去某个buffer里面查看是否有数据,有数据则返回,没数据则休眠

2、input_,manager.c

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h>
#include <string.h>#include <input_manager.h>/* 定义输入设备链表头指针 */
static PInputDevice g_InputDevs  = NULL;/* 关于线程定义的变量 */
static pthread_mutex_t g_tMutex  = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t  g_tConVar = PTHREAD_COND_INITIALIZER;/* 注册输入设备,将其加入链表中 */
void RegisterInputDevice(PInputDevice ptInputDev)
{ptInputDev->ptNext = g_InputDevs;g_InputDevs = ptInputDev;
}/* 初始化输入系统 */
void InputInit(void)
{/* regiseter touchscreen */extern void TouchscreenRegister(void);TouchscreenRegister();/* regiseter netinput */extern void NetInputRegister(void);NetInputRegister();
}/* 输入接收线程函数 */
static void *input_recv_thread_func (void *data)
{PInputDevice ptInputDev = (PInputDevice)data;InputEvent tEvent;int ret;while (1){/* 读数据 */ret = ptInputDev->GetInputEvent(&tEvent);if (!ret){	/* 保存数据 */pthread_mutex_lock(&g_tMutex);PutInputEventToBuffer(&tEvent);/* 唤醒等待数据的线程 */pthread_cond_signal(&g_tConVar); /* 通知接收线程 */pthread_mutex_unlock(&g_tMutex);}}return NULL;
}/* 初始化输入设备 */
void IntpuDeviceInit(void)
{int ret;pthread_t tid;/* for each inputdevice, init, pthread_create *//* 遍历所有输入设备,分别进行初始化和创建线程 */PInputDevice ptTmp = g_InputDevs;while (ptTmp){/* 初始化设备 */ret = ptTmp->DeviceInit();/* 创建线程 */if (!ret){ret = pthread_create(&tid, NULL, input_recv_thread_func, ptTmp);}ptTmp= ptTmp->ptNext;}
}/* 获取输入事件 */
int GetInputEvent(PInputEvent ptInputEvent)
{InputEvent tEvent;int ret;/* 无数据则休眠 */pthread_mutex_lock(&g_tMutex);if (GetInputEventFromBuffer(&tEvent))//读取数据成功(有数据可读){*ptInputEvent = tEvent;//保存数据pthread_mutex_unlock(&g_tMutex);return 0;}else//读取数据失败(没数据可读){/* 休眠等待 */pthread_cond_wait(&g_tConVar, &g_tMutex);	if (GetInputEventFromBuffer(&tEvent))//有数据杯唤醒之后,读取数据{*ptInputEvent = tEvent;//保存数据ret = 0;}else{ret = -1;}pthread_mutex_unlock(&g_tMutex);		}return ret;}

怎么避免数据丢失?
比如触摸屏,它一下子会上报很多数据,对于网络输入,也有可能会一次性有很多客户端发来数据,所以不能使用变量来保存数据,要使用一个“唤醒缓冲区

3、环形缓冲区

环形缓冲去其实也是一个数组,只不过是一个“高级”数据罢了!
例:char buf[5];
在这里插入图片描述
这里的满是指还有一个位置可以填!

4、input_,manager.c加入环形Buffer操作函数

#include <pthread.h>          /* 包含线程相关函数和数据结构头文件 */
#include <stdio.h>            /* 包含标准输入输出函数头文件 */
#include <unistd.h>           /* 包含UNIX标准函数头文件 */
#include <semaphore.h>        /* 包含信号量相关函数和数据结构头文件 */
#include <string.h>           /* 包含字符串处理函数头文件 */
#include <input_manager.h>    /* 包含输入管理器相关函数和数据结构头文件 *//* 全局输入设备链表头指针 */
static PInputDevice g_InputDevs = NULL;/* 互斥锁和条件变量,用于线程同步 */
static pthread_mutex_t g_tMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t g_tConVar = PTHREAD_COND_INITIALIZER;/* 环形缓冲区相关定义 */
#define BUFFER_LEN 20
static int g_iRead = 0;
static int g_iWrite = 0;
static InputEvent g_atInputEvents[BUFFER_LEN];/* 环形缓冲区操作函数 *//* 判断唤醒缓冲区是否为满 */
//满了返回1,没满返回0
static int isInputBufferFull(void)
{return (g_iRead == ((g_iWrite + 1) % BUFFER_LEN));
}/* 判断唤醒缓冲区是否为空 */
//空了返回1,没空返回0
static int isInputBufferEmpty(void)
{return (g_iRead == g_iWrite);
}//储存输入事件到环形缓冲区里面
static void PutInputEventToBuffer(PInputEvent ptInputEvent)
{if (!isInputBufferFull())//数组不满,就储存{g_atInputEvents[g_iWrite] = *ptInputEvent;g_iWrite = (g_iWrite + 1) % BUFFER_LEN;}
}//在环形缓冲区里面读取输入事件
static int GetInputEventFromBuffer(PInputEvent ptInputEvent)
{if (!isInputBufferEmpty())//数组不空就读取{*ptInputEvent = g_atInputEvents[g_iRead];g_iRead = (g_iRead + 1) % BUFFER_LEN;return 1;}else{return 0;}
}/* 注册输入设备函数 */
void RegisterInputDevice(PInputDevice ptInputDev)
{ptInputDev->ptNext = g_InputDevs;g_InputDevs = ptInputDev;
}/* 初始化输入系统 */
void InputInit(void)
{/* 注册触摸屏设备 */extern void TouchscreenRegister(void);TouchscreenRegister();/* 注册网络输入设备 */extern void NetInputRegister(void);NetInputRegister();
}/* 输入接收线程函数 */
static void *input_recv_thread_func(void *data)
{PInputDevice ptInputDev = (PInputDevice)data;InputEvent tEvent;int ret;while (1){/* 从输入设备读取数据 */ret = ptInputDev->GetInputEvent(&tEvent);if (!ret){/* 保存数据到环形缓冲区 */pthread_mutex_lock(&g_tMutex);PutInputEventToBuffer(&tEvent);/* 唤醒等待数据的线程 */pthread_cond_signal(&g_tConVar);pthread_mutex_unlock(&g_tMutex);}}return NULL;
}/* 初始化输入设备 */
void IntpuDeviceInit(void)
{int ret;pthread_t tid;/* 遍历所有输入设备,进行初始化和创建接收线程 */PInputDevice ptTmp = g_InputDevs;while (ptTmp){/* 初始化设备 */ret = ptTmp->DeviceInit();/* 创建接收线程 */if (!ret){ret = pthread_create(&tid, NULL, input_recv_thread_func, ptTmp);}ptTmp = ptTmp->ptNext;}
}/* 获取输入事件 */
int GetInputEvent(PInputEvent ptInputEvent)
{InputEvent tEvent;int ret;/* 无数据则休眠 */pthread_mutex_lock(&g_tMutex);if (GetInputEventFromBuffer(&tEvent)){*ptInputEvent = tEvent;pthread_mutex_unlock(&g_tMutex);return 0;}else{/* 休眠等待 */pthread_cond_wait(&g_tConVar, &g_tMutex);if (GetInputEventFromBuffer(&tEvent)){*ptInputEvent = tEvent;ret = 0;}else{ret = -1;}pthread_mutex_unlock(&g_tMutex);}return ret;
}

七、输入管理单元测试

1、input_test.c

#include <sys/mman.h> // 内存管理声明
#include <sys/types.h> // 基本系统数据类型
#include <sys/stat.h> // 文件状态定义
#include <unistd.h> // 提供通用的文件、目录、程序及进程操作的函数
#include <linux/fb.h> // 帧缓冲设备的定义
#include <fcntl.h> // 文件控制选项定义
#include <stdio.h> // 标准输入输出定义
#include <string.h> // 字符串操作函数定义
#include <sys/ioctl.h> // IO控制设备的函数定义#include <input_manager.h> // 自定义输入管理的头文件int main(int argc, char **argv)
{int ret; // 用于函数返回值InputEvent event; // 定义一个输入事件的结构体变量InputInit(); // 初始化输入系统IntpuDeviceInit(); // 初始化输入设备,注意这里应该是 InputDeviceInitwhile (1) // 主循环{// 打印当前文件名、函数名和行号printf("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);ret = GetInputEvent(&event); // 从输入设备获取一个事件// 再次打印,并显示GetInputEvent函数的返回值printf("%s %s %d, ret = %d\n", __FILE__, __FUNCTION__, __LINE__, ret);if (ret) { // 如果返回值不是0,表示获取事件时出错printf("GetInputEvent err!\n");return -1; // 出错返回-1}else // 如果成功获取事件{// 打印事件类型printf("%s %s %d, event.iType = %d\n", __FILE__, __FUNCTION__, __LINE__, event.iType );if (event.iType == INPUT_TYPE_TOUCH) // 如果是触摸屏事件{// 打印触摸屏事件的详细信息printf("Type      : %d\n", event.iType);printf("iX        : %d\n", event.iX);printf("iY        : %d\n", event.iY);printf("iPressure : %d\n", event.iPressure);}else if (event.iType == INPUT_TYPE_NET) // 如果是网络事件{// 打印网络事件的详细信息printf("Type      : %d\n", event.iType);printf("str       : %s\n", event.str);}}}return 0; // 正常退出程序	
}

2、input下的Makefile

EXTRA_CFLAGS  := 
CFLAGS_file.o := obj-y += touchscreen.o
obj-y += netinput.o
obj-y += input_manager.o

3、uinttest下的Makefile

EXTRA_CFLAGS  := 
CFLAGS_file.o := #obj-y += disp_test.o
obj-y += input_test.o

4、上板测试

在这里插入图片描述
在这里插入图片描述

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

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

相关文章

数据库设计与开发—初识SQLite与DbGate

一、SQLite与DbGate简介 &#xff08;一&#xff09;SQLite[1][3] SQLite 是一个部署最广泛、用 C 语言编写的数据库引擎&#xff0c;属于嵌入式数据库&#xff0c;其作为库被软件开发人员嵌入到应用程序中。 SQLite 的设计允许在不安装数据库管理系统或不需要数据库管理员的情…

sublime配置(竞赛向)

我也想要有jiangly一样的sublime 先决条件 首先&#xff0c;到官网上下载最新的sublime4&#xff0c;然后在mingw官网上下载最新的mingw64 mingw64官网&#xff1a;左边菜单栏点击dowloads,然后选择MinGW-W64-builds(可能会有点慢)——然后有时候会变成选LLVM-minGW,接着选择…

linux c国际化

一种locale表示一种文化的各种数据的表示或显示方式&#xff0c;一种locale分成多个部分&#xff0c;不同的部分由category表示&#xff0c;每一种category下面定义了很多关键字keyword locale -a 查看所有支持的locale&#xff0c; locale 不带参 查看当前locale的各个categ…

大语言模型怎么写好提示词,看这篇就够了

对于任何输入&#xff0c;大语言模型都会给出相应的输出&#xff0c;这些输入都可以成为提示词&#xff0c;通常&#xff0c;提示词由指令和输入数据组成&#xff0c;指令是任务&#xff0c;输入数据是完成的要求&#xff0c;其中指令应该明确&#xff0c;用词不能模棱两可&…

centos7.9升级rockylinux8.8

前言 查看centos的版本 &#xff0c;我这台服务器是虚拟机,下面都是模拟实验 升级前一定要把服务器上配置文件&#xff0c;数据等进行备份 [rootlocalhost ~]#cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) [rootlocalhost ~]#uname -a Linux jenkins_ser…

【C++进阶】AVL树的实现

1. AVL的概念 AVL树是最先发明的⾃平衡⼆叉查找树&#xff0c;AVL是⼀颗空树&#xff0c;或者具备下列性质的⼆叉搜索树&#xff1a;它的左右⼦树都是AV树&#xff0c;且左右⼦树的⾼度差的绝对值不超过1。AVL树是⼀颗⾼度平衡搜索⼆叉树&#xff0c;通过控制⾼度差去控制平衡…

SLM201A系列24V, 15mA - 60mA单通道线性恒流LED驱动芯片 灯带灯条解决方案

SLM201A系列型号&#xff1a; SLM201A15aa-7G SLM201A20aa-7G SLM201A25aa-7G SLM201A30aa-7G SLM201A35aa-7G SLM201A40aa-7G SLM201A45aa-7G SLM201A50aa-7G SLM201A55aa-7G SLM201A60aa-7G SLM201A 系列产品是用于产生单通道、高…

基于FPGA的以太网设计(一)

以太网简介 以太网&#xff08;Ethernet&#xff09;是一种计算机局域网技术。IEEE组织的IEEE 802.3标准制定了以太网的技术标准&#xff0c;它规定了包括物理层的连线、电子信号和介质访问控制的内容。以太网是目前应用最普遍的局域网技术&#xff0c;取代了其他局域网标准如…

【unity小技巧】Unity6 LTS版本安装和一些修改和新功能使用介绍

文章目录 前言安装新功能变化1、官方推荐使用inputsystem进行输入控制2、修复了InputSystem命名错误导致listen被遮挡的bug3、自带去除unity启动画面logo功能4、unity官方的behavior行为树插件5、linearVelocity代替过时的velocity方法待续 完结 前言 2024/10/17其实unity就已…

gitlab:ssh设置

我用的是window&#xff0c;先打开终端&#xff1a; 1、输入 ssh-skygen 执行 然后输入路径&#xff0c;路径地址就是后面括号内的内容 2、然后直接下一步下一步即可&#xff0c;像上面那样就成了 3、打开公钥&#xff0c;复制 4、打开gitlab&#xff0c;在我的 Edit profil…

JUnit 单元测试(详解)

&#x1f680; 个人简介&#xff1a;某大型国企资深软件开发工程师&#xff0c;信息系统项目管理师、CSDN优质创作者、阿里云专家博主&#xff0c;华为云云享专家&#xff0c;分享前端后端相关技术与工作常见问题~ &#x1f49f; 作 者&#xff1a;码喽的自我修养&#x1f9…

shell脚本宝藏仓库(基础命令、正则表达式、shell基础、变量、逻辑判断、函数、数组)

一、shell概述 1.1 shell是什么 Shell是一种脚本语言 脚本&#xff1a;本质是一个文件&#xff0c;文件里面存放的是特定格式的指令&#xff0c;系统可以使用脚本解析器、翻译或解析指令并执行&#xff08;shell不需要编译&#xff09; Shell既是应用程序又是一种脚本语言&…

C++中类间相互引用与析构函数调用的潜在风险及解决方案

C中类间相互引用与析构函数调用的潜在风险及解决方案 一、前言二、举例说明三、问题分析四、解决方案 一、前言 在C中&#xff0c;当两个类A和B之间存在相互引用&#xff0c;并且在A的析构函数中调用B的成员函数&#xff0c;同时B的成员函数又尝试访问A的对象或调用A的成员函数…

《深度学习》Dlib、OpenCV 轮廓绘制

目录 一、Dlib轮廓绘制 1、什么是轮廓绘制 2、步骤 1&#xff09;导入所需的库和模型 2&#xff09;加载人脸检测器 3&#xff09;读取图像 4&#xff09;人脸检测 5&#xff09;关键点定位 6&#xff09;绘制轮廓线条 7&#xff09;展示结果 二、案例实现 1、完整代码 运…

【华为】静态NAT、动态NAT、NAPT、Easy IP、NAT Server

静态 NAT&#xff1a;将内网主机的私网 IP地址一对一映射到公网 IP 地址。动态 NAT&#xff1a;将内网主机的私有地址转换为公网地址池里面的地址。由于静态NAT严格地一对一进行地址映射&#xff0c;这就导致即便内网主机长时间离线或者不发送数据时&#xff0c;与之对应的公有…

Mac 电脑安装redis

1、首先检查电脑是否安装 brew 命令&#xff1a; #打开Mac自带的终端&#xff0c;输入下面命令 brew --version如下图&#xff0c;可以看到我的 brew 正常的&#xff0c;且对应版本是4.0.17-63-g32f2258 如果你的电脑执行上面命名报错&#xff1a;zsh: command not found: br…

一个很恶心但发顶会很牛的方向!【小样本学习+目标检测】

【小样本学习目标检测】致力于通过有限的标注样本实现高效的目标检测&#xff0c;以应对数据匮乏的挑战。这一领域的研究对于缩小人工智能与人类学习系统之间的差异、增强模型对新类别的适应能力、推动智能识别系统在实际场景中的应用具有重要意义。 为了帮助研究人员深入理解…

【可答疑】基于51单片机的智能衣柜(含仿真、代码、报告、演示视频等)

✨哈喽大家好&#xff0c;这里是每天一杯冰美式oh&#xff0c;985电子本硕&#xff0c;大厂嵌入式在职0.3年&#xff0c;业余时间做做单片机小项目&#xff0c;有需要也可以提供就业指导&#xff08;免费&#xff09;~ &#x1f431;‍&#x1f409;这是51单片机毕业设计100篇…

多进程编辑

使用父子进程完成两个文件的拷贝&#xff0c;父进程拷贝前一半&#xff0c;子进程拷贝后一半&#xff0c;两个进程同时进行 #include <myhead.h> int main(int argc, const char *argv[]) {//用于保存pid号pid_t pid -1;//创建子进程pid fork();//打印一下进程号print…

C语言 | Leetcode C语言题解之第491题非递减子序列

题目&#xff1a; 题解&#xff1a; int** ans; int ansSize; int* temp; int tempSize;void dfs(int cur, int last, int* nums, int numsSize, int** returnColumnSizes) {if (cur numsSize) {if (tempSize > 2) {ans[ansSize] malloc(sizeof(int) * tempSize);memcpy(…