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

news2024/11/29 13:36:13

文章目录

  • 前言
  • 一、数据结构抽象
    • 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   2


typedef 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;

    //读取成功,返回0
	return 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 1

int 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)nm
 
STRIP		= $(CROSS_COMPILE)strip
OBJCOPY		= $(CROSS_COMPILE)objcopy
OBJDUMP		= $(CROSS_COMPILE)objdump
 
export AS LD CC CPP AR NM
export STRIP OBJCOPY OBJDUMP
 
CFLAGS := -Wall -O2 -g
CFLAGS += -I $(shell pwd)/include
 
LDFLAGS := -lts
 
export CFLAGS LDFLAGS
 
TOPDIR := $(shell pwd)
export TOPDIR
 
TARGET := test
 
 
obj-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;
	}
	else
		return -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 1

int 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 8888

int 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 0
	iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));	
	if (-1 == iRet)
	{
		printf("connect error!\n");
		return -1;
	}
#endif

	iAddrLen = 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(); // 初始化输入设备,注意这里应该是 InputDeviceInit
 
	while (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.coloradmin.cn/o/2206151.html

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

相关文章

5.STM32的串口通信

5.STM32的串口通信 两个串口之间的通信 发送端口&#xff1a;TX 接受端口&#xff1a;RX 注意一台设备的TX 与 另一台RX相连接 共地&#xff1a;还需要将两端地线相连接&#xff0c;将设备的参考电势在同一水平&#xff08;通讯的前提&#xff09; 建立连接 在cubeIDE中 图…

独家揭秘!新手铲屎官不可错过的宠物空气净化器选购攻略就在这

终于给我等到了双十一&#xff0c;这可是一年一度的促销力度最大的购物狂欢节&#xff0c;不要问我为什么这么期待&#xff0c;因为我养了猫&#xff0c;猫咪掉毛太严重了&#xff0c;必须得买一个宠物空气净化器&#xff0c;不然我在这个家就呆不下去了。 最近国庆节刚带猫回…

Transactional注解导致Spring Bean定时任务失效

背景 业务需要定时捞取数据库中新增的数据做数据处理及分析&#xff0c;更新状态&#xff0c;处理结束。而我们不能随意定义线程池&#xff0c;规定使用统一的标准规范来定义线程池。如在配置文件中配置线程池的属性&#xff1a;名称&#xff0c;线程核心数等&#xff0c;任务…

用最短长度的绳子把整个花园围起来

给定一个数组 trees&#xff0c;其中 trees[i] [xi, yi] 表示树在花园中的位置。 你被要求用最短长度的绳子把整个花园围起来&#xff0c;因为绳子很贵。只有把 所有的树都围起来&#xff0c;花园才围得很好。 返回恰好位于围栏周边的树木的坐标。 示例 1: 输入: points […

白鲨优化算法(WSO)的MATLAB代码复现

目录 1 白鲨优化算法优化BP神经网络代码复现 2 白鲨优化算法优化支持向量机代码复现 3 白鲨优化算法优化长短期记忆神经网络代码复现 1 白鲨优化算法优化BP神经网络代码复现 1&#xff09;单输出回归预测&#xff1a;单输出回归预测&#xff1a;大白鲨算法优化BP神经网络模…

OpenCV HoughLine()函数与HoughlinesP()函数及HoughCircles()函数详解及用法示例

OpenCV HoughLine()函数与HoughlinesP()函数都用于图像中的直线检测&#xff0c;但二者是有区别的。 HoughLine()函数 HoughLines&#xff08;&#xff09;基于霍夫变换的原理&#xff0c;通过投票机制来确定图像中直线的存在及其参数。该函数返回检测到的直线的极坐标参数。它…

【多线程】CAS原理

文章目录 为什么会出现CAS思想?CAS概念CAS自旋概念CAS的简单使用CAS源码解析 UnSafe类CAS底层原理CAS的硬件保证CAS自旋锁的实现前置知识----原子引用AtomicReference实现自旋锁 CAS缺点ABA问题什么是ABA问题如何解决ABA问题简单案例AtomicStampedReference的源码分析 为什么会…

leetcode链表(二)-两两交换链表中的节点

题目 . - 力扣&#xff08;LeetCode&#xff09; 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 思路 一定要使用虚拟头节点…

电子学报期刊投稿过程记录

电子学报的编辑老师确实人非常好&#xff0c;专业知识过硬&#xff0c;文章内容审核仔细&#xff0c;对格式的要求相对严格&#xff0c;并且打电话或者邮箱询问都很和善&#xff0c;也很温柔&#xff0c;同时也愿意配合再缴费后提前发送录用证明&#xff0c;但是见刊和网络首发…

单点登录Apereo CAS 7.1客户端集成教程

从上一篇部署并成功运行CAS服务端后,我们已经能通过默认的账号密码进行登录。 上篇地址:单点登录Apereo CAS 7.1安装配置教程-CSDN博客 本篇我们将开始对客户端进行集成。 CAS中的客户端,就是指我们实际开发的各个需要登录认证的应用。现在,跟着笔者的步伐,一起探索如何…

共识算法Raft

引入 在分布式系统中&#xff0c;为了消除单点提高系统可用性&#xff0c;通常会创建副本来进行容错&#xff0c;但这会带来另一个问题就是&#xff0c;如何保证多个副本之间的数据一致性。 为了解决这个问题&#xff0c;计算机行内就提出了共识算法&#xff0c;它允许多个分…

git gui基本使用

一、图形化界面 二、创建新项目 创建文件&#xff0c;加入暂存区&#xff0c;提交到版本库 三、创建分支 四、合并分支 1.切换至master 五、更新分支 六、解决冲突 修改冲突&#xff0c;加入暂存区&#xff0c;提交到版本库 七、远程创建库 Gitee - 基于 Git 的代码托管和研…

低功耗

低功耗 目录 低功耗 STM32中的电源系统 STM32 中的低功耗 相关代码 -- 首先我们先看我们做的项目如何降低功耗 -- 对于设备&#xff0c;功耗怎么降低&#xff1f;把设备上所有的电子模块&#xff0c;都进入低功耗模式。 对于空气质量检测仪&#xff0c;如何降低功耗&…

修改armbian DNS服务器地址(永久修改DNS配置)

linux dns服务器地址的配置文件在/etc/resolv.conf 但系统可能设置的是默认值&#xff0c;也就是192.168.1.1。导致系统无法正常解析域名&#xff0c;进而导致有一些接口无法调用或下载失败。 最直接的思路就是修改/etc/resolv.conf&#xff0c;将其中的nameserver修改为正确的…

必看系列:面试官通过一个问题考查了网络编程所有知识点!

一、写在开头 本文的主题是和大家一起探讨学习:“在浏览器中输入URL开始后,计算机所做的几件事”,这个问题是好几年前自己面试的时候,面试官考问过的,当时准备十分不充分,回答的一塌糊涂,今天拿出来再整理学习一遍,一同进步! 其实这个问题本身倒是不难,但它巧妙的是…

节假日提醒,节假日任务,节假日判断如何做?这篇文章教会你!

你是否有这样的需求&#xff0c;有一个任务需要在大家都休息的时候处理&#xff0c;你肯定会想到周六周日了&#xff0c;那不好意思&#xff0c;遇到调休怎么办呢&#xff1f;遇到国假怎么办呢&#xff1f;我这里所说的节假日和工作日不仅仅指正常的周一至周日&#xff0c;还包…

浙江省发规院产业发展研究所调研组莅临迪捷软件考察调研

2024年10月10日下午&#xff0c;浙江省发展与规划院产业发展研究所调研组一行莅临迪捷软件考察调研&#xff0c;绍兴市府办、区发改、区经信、迪荡街道等相关领导陪同。 调研组一行参观了迪捷软件的展厅与办公区&#xff0c;深入了解了迪捷软件的公司发展历程、运营状况、产品…

Python 如何使用 Bert 进行中文情感分析

前言 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;情感分析是一个非常常见且重要的应用。情感分析通常用于识别文本中的情感&#xff0c;例如判断一条微博或评论是正面、负面还是中性。在过去的几年中&#xff0c;随着深度学习的发展&#xff0c;BERT&#xff…

MySQL表的基本操作和数据类型

MySQL表的基本操作和数据类型 表的操作创建表修改表删除表 数据类型数值类型整型浮点型 文本、二进制类型日期时间类型ENUM类型和SET类型 表的操作 创建表 语法&#xff1a; CREATE TABLE table_name(field1 datatype,field2 datatype,field3 datatype )character set 字符集…

Python的matplotlib可视化工具基本操作(数据分析生成图表)

一、安装导入 1、使用包管理器安装matplotlib pip3 install matplotlib 2、导入plt工具 import matplotlib.pyplot as plt 二、基本函数 1、创建图表 使用pyplot工具打点调用创建图表函数 例如创建直方图&#xff1a; import matplotlib.pyplot as plt import pandas…