iMX6ULL应用移植 | 移植 infoNES 模拟器(重玩经典NES游戏)

没玩过NES游戏的童年,可能不是80后的童年。我们小时候是从玩FC开始接触游戏机的,那时真的是红极一时啊,我上初中时还省吃俭用买了一台小霸王,暑假里把电视机都给打爆了!那时任天堂单是FC机的主机的发售收入就超过全美的电视台的收入的总和,在人们的心目中扎下了任天堂的这个招牌。

前言

1983年7月15日,由日本任天堂株式会社(原本是生产日式扑克即“花札”)的宫本茂先生领导开发的一种第三代家用电子游戏机:FC,全称:Family Computer,也称作:Famicom;在欧美发售时则被称为nes,全称:Nintendo Entertainment System;在中国大陆、台湾和香港等地,因其外壳为红白两色,所以人们俗称其为“红白机”,正式进入市场销售,并于后来取得了巨大成功,由此揭开了家用电子游戏机遍布世界任何角落,电子游戏全球大普及的序幕。

什么是InfNES?

一款NES游戏模拟器。InfoNES可以很容易地被移植到各个平台,作者是Martin Freij。他是一位瑞典的程序员和游戏爱好者,于2002年开发了infoNES模拟器。infoNES是一个基于NES(任天堂娱乐系统)的模拟器,旨在让人们能够在计算机上玩经典的NES游戏。

InfoNES具备良好的可移植性,它将与环境有关的内容都清出了软件内核,并且单独集合于一个InfoNES_System.h中,我们要做的就是实现这里提到的各种函数,再把InfoNES加入到我们的工程中一起编译。

最近成功实现了USB接口的FC手柄驱动,使得在imx6ull开发板玩游戏具有可玩性,这里将这个移植过程记录下来。如果对NES模拟器的源码实现感兴趣,infoNES也是个不错的研究对象,代码结构清晰,可以让你了解到如何模拟实现k6502这款经典cpu的,加深对计算机体系结构的理解。

接下来让我们重温下经典,缅怀下童年吧!

池塘外的迷路书上,知鸟在声声叫着夏天......,伴随着优美的歌声,仿佛穿越回来了,少年。

完成以下操作,让你即刻拥有款移动游戏机,实现童年时的梦想。

很早之前我在imax283平台上移植过infoNES,那时我的github仓地址是:

https://github.com/yongzhena/infoNES

这次直接拉取下来用,只是修改下joypad手柄驱动的代码就可以完美运行啦。

移植过程

整个移植过程主要涉及三部分,显示、声音输出和usb手柄支持。前两个直接拉取上面的我的仓直接就具备了,这里着重介绍下USB手柄驱动支持。

基于fb0的LCD显示

在InfoNES_System_Linux.cpp文件中修改。显示这块儿实现两个函数,一个是lcd_fb_init,一个是lcd_fb_display_px。

static int lcd_fb_init()
{//如果使用 mmap 打开方式 必须是 读定方式fb_fd = open("/dev/fb0", O_RDWR);if(-1 == fb_fd){printf("cat't open /dev/fb0 \n");return -1;}//获取屏幕参数if(-1 == ioctl(fb_fd, FBIOGET_VSCREENINFO, &var)){close(fb_fd);printf("cat't ioctl /dev/fb0 \n");return -1;}//计算参数px_width     = var.bits_per_pixel /8;line_width   = var.xres * px_width;screen_width = var.yres * line_width;lcd_width    = var.xres;lcd_height   = var.yres;printf("fb width:%d height:%d pixel:%d \n", lcd_width, lcd_height,px_width*8);fb_mem = (unsigned char *)mmap(NULL, screen_width, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);if(fb_mem == (void *)-1){close(fb_fd);printf("cat't mmap /dev/fb0 \n");return -1;}//清屏memset(fb_mem, 0 , screen_width);return 0;
}
static int lcd_fb_display_px(WORD color, int x, int y)
{unsigned char  *pen8;unsigned short *pen16;pen8 = (unsigned char *)(fb_mem + y*line_width + x*px_width);pen16 = (unsigned short *)pen8;*pen16 = color;return 0;
}

以下的实现注意zoom_x_tab,zoom_y_tab这两项。它的作用是对像素做了全屏和放大处理。 源码里的make_zoom_tab()就是干这个用。如果觉得屏幕很大,放大后颗粒感很重,能否再优化?这里是个可能的优化方向。

/*===================================================================*/
/*                                                                   */
/*      InfoNES_LoadFrame() :                                        */
/*           Transfer the contents of work frame on the screen       */
/*                                                                   */
/*===================================================================*/
unsigned short ChColor(unsigned short color)
{return (color>>3)<<4|(color&0x001f);
}void InfoNES_LoadFrame()
{int x,y;int line_width;WORD wColor,R,G,B,Gr;//修正 if(0 < fb_fd){for (y = 0; y < lcd_height; y++ ){line_width = zoom_y_tab[y] * NES_DISP_WIDTH;for (x = 0; x < lcd_width; x++ ){wColor = ChColor(WorkFrame[line_width  + zoom_x_tab[x]]);lcd_fb_display_px(wColor, x, y);}}}/*16 bit per pixel*//* Exchange 16-bit to 256 gray *//*for (y = 0; y < NES_DISP_HEIGHT; y++ ){for (x = 0; x < NES_DISP_WIDTH; x++ ){//wColor = WorkFrame[y * lcd_width  + x ];wColor = WorkFrame[ ( y << 8 ) + x ];R = ( ( wColor & 0x7c00 ) >>7 );G = ( ( wColor & 0x03e0 ) >>2 );B = ( ( wColor & 0x001f ) <<3 );            //Gr= ( ( 9798*R + 19235*G + 3735*B)>>15);wColor=(WORD)((B<<16)|(G<<8)|R);lcd_fb_display_px(wColor, x, y);}}*/  
}

基于Alsa的声音支持

实现这个声音支持的前提是,板子上得有基于alsa框架的音频驱动且功能正常。否则以下这些实现里需要全部留空,不用实现。

/*===================================================================*/
/*                                                                   */
/*        InfoNES_SoundInit() : Sound Emulation Initialize           */
/*                                                                   */
/*===================================================================*/
void InfoNES_SoundInit( void )
{}/*===================================================================*/
/*                                                                   */
/*        InfoNES_SoundOpen() : Sound Open                           */
/*                                                                   */
/*===================================================================*/
int InfoNES_SoundOpen( int samples_per_sync, int sample_rate )
{// sample_rate 采样率 44100// samples_per_sync  735// 采样率 / 8 * 声道数 = 44100 / 8 * 1 = 5512.5// 8位 声音/*声道数 1采样率 44100采样位数 8每次播放块大小(NES  APU 每次生成一块)735*/unsigned int rate      = sample_rate;snd_pcm_hw_params_t *hw_params;if(0 > snd_pcm_open(&playback_handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) {printf("snd_pcm_open err\n");return -1;}printf("snd_pcm_open ok!\nsamples_per_sync=%d,sample_rate=%d\n",samples_per_sync,sample_rate);if(0 > snd_pcm_hw_params_malloc(&hw_params)){printf("snd_pcm_hw_params_malloc err\n");return -1;}if(0 > snd_pcm_hw_params_any(playback_handle, hw_params)){printf("snd_pcm_hw_params_any err\n");return -1;}if(0 > snd_pcm_hw_params_set_access(playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) {printf("snd_pcm_hw_params_any err\n");return -1;}//16bit PCM 数据if(0 > snd_pcm_hw_params_set_format(playback_handle, hw_params, SND_PCM_FORMAT_U8)){printf("snd_pcm_hw_params_set_format err\n");return -1;}if(0 > snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, &rate, 0)) {printf("snd_pcm_hw_params_set_rate_near err\n");return -1;}//单声道 非立体声if(0 > snd_pcm_hw_params_set_channels(playback_handle, hw_params, 1)){printf("snd_pcm_hw_params_set_channels err\n");return -1;}if(0 > snd_pcm_hw_params(playback_handle, hw_params)) {printf("snd_pcm_hw_params err\n");return -1;}snd_pcm_hw_params_free(hw_params);if(0 > snd_pcm_prepare(playback_handle)) {printf("snd_pcm_prepare err\n");return -1;}return 1;
}/*===================================================================*/
/*                                                                   */
/*        InfoNES_SoundClose() : Sound Close                         */
/*                                                                   */
/*===================================================================*/
void InfoNES_SoundClose( void )
{snd_pcm_close(playback_handle);
}/*===================================================================*/
/*                                                                   */
/*            InfoNES_SoundOutput() : Sound Output 5 Waves           */
/*                                                                   */
/*===================================================================*/
void InfoNES_SoundOutput( int samples, BYTE *wave1, BYTE *wave2, BYTE *wave3, BYTE *wave4, BYTE *wave5 )
{int i;int ret;unsigned char wav;unsigned char *pcmBuf = (unsigned char *)malloc(samples);//printf("InfoNES_SoundOutput,samples=%d\n",samples);//printf("\n");for (i=0; i <samples; i++){wav = (wave1[i] + wave2[i] + wave3[i] + wave4[i] + wave5[i]) / 5;//单声道 8位数据pcmBuf[i] = wav;//printf("%02x",wav);}//printf("\n");ret = snd_pcm_writei(playback_handle, pcmBuf, samples);if(-EPIPE == ret){snd_pcm_prepare(playback_handle);}free(pcmBuf);return ;
}

USB手柄支持

接下来这块儿是介绍的重点,实现usb手柄驱动的支持。这样才有可玩性啊。我买的这款USB的游戏手柄很便宜,也很容易买到。如果你的USB手柄不是这款,那么实现驱动支持的原理也是类似的,万变不离宗,只是键值对应关系跟我的可能不一样,实测改下即可。

关于USB游戏手柄的驱动支持,参见我的上篇博文:iMX6ULL驱动开发 | 让imx6ull开发板支持usb接口FC游戏手柄_特立独行的猫a的博客-CSDN博客

不想按上文总结的重新编译内核的话,可以把驱动单独编译成模块动态加载进去。

这里介绍下让infoNES支持usb手柄需要做哪些移植。

按键键值测试小程序

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h> #define _EV_KEY         0x01    /* button pressed/released */
#define _EV_ABS         0x03    
#define _EV_MSC         0x04   int main() {printf("hello,usb hid joystick key test\n");int fd = open("/dev/input/event3", O_RDONLY);struct input_event e;while(1) {read(fd, &e, sizeof(e));switch(e.type) {case _EV_KEY:printf("type: %d, code: %d,value: %d, time: %d\n", e.type, e.code,e.value, e.time);break;case _EV_ABS:printf("type: %d, code: %d,value: %d, time: %d\n", e.type, e.code,e.value, e.time);break;case _EV_MSC:printf("type: %d, code: %d,value: %d, time: %d\n", e.type, e.code,e.value, e.time);break;default:if(e.type != 0){printf("type:%d, code: %d,value: %d, time: %d\n",e.type, e.code,e.value, e.time);}}}close(fd);return 0;
}

joypad_input.cpp文件修改

主要是USBjoypadGet()接口的实现,要跟FC手柄的键值对应上。

static int USBjoypadGet(void)
{/*** FC手柄 bit 键位对应关系 真实手柄中有一个定时器,处理 连A  连B * 0  1   2       3       4    5      6     7* A  B   Select  Start  Up   Down   Left  Right*///因为 USB 手柄每次只能读到一位键值 所以要有静态变量保存上一次的值static unsigned char joypad = 0;struct input_event e;if(0 < read (USBjoypad_fd, &e, sizeof(e))){if(0x3 == e.type){/*上:value:0 type:0x3 code:0x1value:127 type:0x3 code:0x1*/if(0 == e.value && 0x1 == e.code){joypad |= 1<<4;printf("Up\n");}/*下:value:255 type:0x3 code:0x1value:127 type:0x3 code:0x1*/if(255 == e.value && 0x1 == e.code){joypad |= 1<<5;printf("Down\n");}//松开if(127 == e.value && 0x1 == e.code){joypad &= ~(1<<4 | 1<<5);}/*左:value:0 type:0x3 code:0x0value:127 type:0x3 code:0x0*/if(0 == e.value && 0 == e.code){joypad |= 1<<6;printf("Left\n");}/*右:value:255 type:0x3 code:0x0value:127 type:0x3 code:0x0*/if(255 == e.value && 0 == e.code){joypad |= 1<<7;printf("Right\n");}//松开if(127 == e.value && 0 == e.code){joypad &= ~(1<<6 | 1<<7);}}if(0x1 == e.type){/*选择:value:0x1 type:0x1 code:296value:0x0 type:0x1 code:296*/if(0x1 == e.value && 296 == e.code){joypad |= 1<<2;printf("Select\n");}if(0x0 == e.value && 296 == e.code){joypad &= ~(1<<2);}/*开始:value:0x1 type:0x1 code:297value:0x0 type:0x1 code:297*/if(0x1 == e.value && 297 == e.code){joypad |= 1<<3;printf("Start\n");}if(0x0 == e.value && 297 == e.code){joypad &= ~(1<<3);}/*Avalue:0x1 type:0x1 code:288value:0x0 type:0x1 code:288*/if(0x1 == e.value && 288 == e.code){joypad |= 1<<0;printf("A\n");}if(0x0 == e.value && 288 == e.code){joypad &= ~(1<<0);}/*Bvalue:0x1 type:0x1 code:289value:0x0 type:0x1 code:289*/if(0x1 == e.value && 289 == e.code){joypad |= 1<<1;printf("B\n");}if(0x0 == e.value && 289 == e.code){joypad &= ~(1<<1);}/*Xvalue:0x1 type:0x1 code:290value:0x0 type:0x1 code:290*/if(0x1 == e.value && 290 == e.code){joypad |= 1<<0;printf("X\n");}if(0x0 == e.value && 290 == e.code){joypad &= ~(1<<0);}/*Yvalue:0x1 type:0x1 code:291value:0x0 type:0x1 code:291*/if(0x1 == e.value && 291 == e.code){joypad |= 1<<1;printf("Y\n");}if(0x0 == e.value && 291 == e.code){joypad &= ~(1<<1);}}return joypad;}return -1;
}

完整实现

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <linux/input.h> #define JOYPAD_DEV "/dev/joypad"
#define USB_JS_DEV "/dev/input/event3"typedef struct JoypadInput{int (*DevInit)(void);int (*DevExit)(void);int (*GetJoypad)(void);struct JoypadInput *ptNext;pthread_t tTreadID;     /* 子线程ID */
}T_JoypadInput, *PT_JoypadInput;//全局变量通过互斥体访问
static unsigned char g_InputEvent;static pthread_mutex_t g_tMutex  = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t  g_tConVar = PTHREAD_COND_INITIALIZER;static int joypad_fd;
static int USBjoypad_fd;
static PT_JoypadInput g_ptJoypadInputHead;static void *InputEventTreadFunction(void *pVoid)
{/* 定义函数指针 */int (*GetJoypad)(void);GetJoypad = (int (*)(void))pVoid;while (1){//因为有阻塞所以没有输入时是休眠g_InputEvent = GetJoypad();//有数据时唤醒pthread_mutex_lock(&g_tMutex);/*  唤醒主线程 */pthread_cond_signal(&g_tConVar);pthread_mutex_unlock(&g_tMutex);}
}static int RegisterJoypadInput(PT_JoypadInput ptJoypadInput)
{PT_JoypadInput tmp;if(ptJoypadInput->DevInit()){return -1;}//初始化成功创建子线程 将子项的GetInputEvent 传进来pthread_create(&ptJoypadInput->tTreadID, NULL, InputEventTreadFunction, (void*)ptJoypadInput->GetJoypad);if(! g_ptJoypadInputHead){g_ptJoypadInputHead = ptJoypadInput;}else{tmp = g_ptJoypadInputHead;while(tmp->ptNext){tmp = tmp->ptNext;}tmp->ptNext = ptJoypadInput;}ptJoypadInput->ptNext = NULL;return 0;
}static int joypadGet(void)
{static unsigned char joypad = 0;//printf("joypadGet val:\n");joypad = read(joypad_fd, 0, 0);return joypad;
}static int joypadDevInit(void)
{joypad_fd = open(JOYPAD_DEV, O_RDONLY);if(-1 == joypad_fd){printf("%s dev not found \r\n", JOYPAD_DEV);return -1;}return 0;
}static int joypadDevExit(void)
{close(joypad_fd);return 0;
}static T_JoypadInput joypadInput = {joypadDevInit,joypadDevExit,joypadGet,
};static int USBjoypadGet(void)
{/*** FC手柄 bit 键位对应关系 真实手柄中有一个定时器,处理 连A  连B * 0  1   2       3       4    5      6     7* A  B   Select  Start  Up   Down   Left  Right*///因为 USB 手柄每次只能读到一位键值 所以要有静态变量保存上一次的值static unsigned char joypad = 0;struct input_event e;if(0 < read (USBjoypad_fd, &e, sizeof(e))){if(0x3 == e.type){/*上:value:0 type:0x3 code:0x1value:127 type:0x3 code:0x1*/if(0 == e.value && 0x1 == e.code){joypad |= 1<<4;printf("Up\n");}/*下:value:255 type:0x3 code:0x1value:127 type:0x3 code:0x1*/if(255 == e.value && 0x1 == e.code){joypad |= 1<<5;printf("Down\n");}//松开if(127 == e.value && 0x1 == e.code){joypad &= ~(1<<4 | 1<<5);}/*左:value:0 type:0x3 code:0x0value:127 type:0x3 code:0x0*/if(0 == e.value && 0 == e.code){joypad |= 1<<6;printf("Left\n");}/*右:value:255 type:0x3 code:0x0value:127 type:0x3 code:0x0*/if(255 == e.value && 0 == e.code){joypad |= 1<<7;printf("Right\n");}//松开if(127 == e.value && 0 == e.code){joypad &= ~(1<<6 | 1<<7);}}if(0x1 == e.type){/*选择:value:0x1 type:0x1 code:296value:0x0 type:0x1 code:296*/if(0x1 == e.value && 296 == e.code){joypad |= 1<<2;printf("Select\n");}if(0x0 == e.value && 296 == e.code){joypad &= ~(1<<2);}/*开始:value:0x1 type:0x1 code:297value:0x0 type:0x1 code:297*/if(0x1 == e.value && 297 == e.code){joypad |= 1<<3;printf("Start\n");}if(0x0 == e.value && 297 == e.code){joypad &= ~(1<<3);}/*Avalue:0x1 type:0x1 code:288value:0x0 type:0x1 code:288*/if(0x1 == e.value && 288 == e.code){joypad |= 1<<0;printf("A\n");}if(0x0 == e.value && 288 == e.code){joypad &= ~(1<<0);}/*Bvalue:0x1 type:0x1 code:289value:0x0 type:0x1 code:289*/if(0x1 == e.value && 289 == e.code){joypad |= 1<<1;printf("B\n");}if(0x0 == e.value && 289 == e.code){joypad &= ~(1<<1);}/*Xvalue:0x1 type:0x1 code:290value:0x0 type:0x1 code:290*/if(0x1 == e.value && 290 == e.code){joypad |= 1<<0;printf("X\n");}if(0x0 == e.value && 290 == e.code){joypad &= ~(1<<0);}/*Yvalue:0x1 type:0x1 code:291value:0x0 type:0x1 code:291*/if(0x1 == e.value && 291 == e.code){joypad |= 1<<1;printf("Y\n");}if(0x0 == e.value && 291 == e.code){joypad &= ~(1<<1);}}return joypad;}return -1;
}static int USBjoypadDevInit(void)
{USBjoypad_fd = open(USB_JS_DEV, O_RDONLY);if(-1 == USBjoypad_fd){printf("%s dev not found \r\n", USB_JS_DEV);return -1;}return 0;
}static int USBjoypadDevExit(void)
{close(USBjoypad_fd);return 0;
}static T_JoypadInput usbJoypadInput = {USBjoypadDevInit,USBjoypadDevExit,USBjoypadGet,
};int InitJoypadInput(void)
{int iErr = 0;//iErr = RegisterJoypadInput(&joypadInput);iErr = RegisterJoypadInput(&usbJoypadInput);return iErr;
}int GetJoypadInput(void)
{/* 休眠 */pthread_mutex_lock(&g_tMutex);pthread_cond_wait(&g_tConVar, &g_tMutex);	/* 被唤醒后,返回数据 */pthread_mutex_unlock(&g_tMutex);return g_InputEvent;
}

编译生成

最后,交叉编译生成可执行文件,放到板子上执行即可,插上USB手柄就可以玩啦,运行不错!还很流畅。需要注意的是,为了支持声音,使用了alsa的头文件并链接了libasound库。需确保你的环境里有这个库,没有的话不支持声音输出,可以去掉这个链接。文末有NES游戏的ROM资源。

makefile脚本

#根据实际路径修改工具链路径
CHAIN_ROOT=/opt/yang/imax6ul/gcc-linaro-arm-linux-gnueabihf-4.9-2014.09_linux/bin
CROSS_COMPILE=$(CHAIN_ROOT)/arm-linux-gnueabihf-#CHAIN_ROOT= /home/yang/b503/ctools/gcc-linaro-arm-linux-gnueabihf-4.9-2014.09_linux/bin
#CROSS_COMPILE=$(CHAIN_ROOT)/arm-linux-gnueabihf-
#CROSS_COMPILE = CC     := $(CROSS_COMPILE)gcc
#CC = arm-poky-linux-gnueabi-gcc
TARBALL = InfoNES08J# InfoNES
.CFILES =	./../K6502.cpp \./../InfoNES.cpp \./../InfoNES_Mapper.cpp \./../InfoNES_pAPU.cpp \./InfoNES_System_Linux.cpp joypad_input.cpp.OFILES	=	$(.CFILES:.cpp=.o)CCFLAGS =    -o2 -fsigned-char  -I../
LDFILGS = -lstdc++	-L../libs	# gcc3.x.xall: InfoNESInfoNES: $(.OFILES)$(CC) $(INCLUDES) -o $@ $(.OFILES) $(LDFILGS) -lm  -lpthread -lasound.cpp.o:$(CC) $(INCLUDES) -c $(CCFLAGS) $*.cpp  -o $@clean:rm -f $(.OFILES) ../*~ ../*/*~ corecleanall:rm -f $(.OFILES) ../*~ ../*/*~ core InfoNESrelease: clean alltar:( cd ..; \tar cvf $(TARBALL).tar ./*; \gzip $(TARBALL).tar \)install:install ./InfoNES /usr/local/bin

其他资源

NES红白机全屏显示

NES专题——NES游戏机简介_nesfc_金小庭的博客-CSDN博客

V3S移植nes游戏模拟器(附带游戏合集)_v3s编译游戏模拟器_qq_46604211的博客-CSDN博客

任天堂红白机nes游戏简介 任天堂红白机nes游戏简介

资料:内含众多NES的游戏ROM文件及运行模拟器

链接:https://pan.baidu.com/s/1uXAxLKGmKGwZFB3Yraq8gg  提取码:qxcy 

游戏合集并解压,然后改名为游戏名为英文
链接:https://pan.baidu.com/s/16hIWwYQQEX9aOBDG1dVa0A
提取码:asdf

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

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

相关文章

解决vite+vue3项目npm装包失败

报错如下&#xff1a; Failed to remove some directories [ npm WARN cleanup [ npm WARN cleanup D:\\V3Work\\v3project\\node_modules\\vue, npm WARN cleanup [Error: EPERM: operation not permitted, rmdir D:\V3Work\v3project\node_modules\vue\reactivity\…

socket server服务器开发常见的并发模型

两种高效的事件处理模式 服务器程序通常需要处理三类事件&#xff1a;I/O 事件、信号及定时事件。有两种高效的事件处理模式&#xff1a;Reactor和 Proactor&#xff0c;同步 I/O 模型通常用于实现Reactor 模式&#xff0c;异步 I/O 模型通常用于实现 Proactor 模式。 无论是 …

SpringBoot核心内容梳理

1.SpringBoot是什么? Spring Boot是一个基于Spring框架的快速开发应用程序的工具。它简化了Spring应用程序的创建和开发过程&#xff0c;使开发人员能够更快速地创建独立的、生产就绪的Spring应用程序。它采用了“约定优于配置”的原则&#xff0c;尽可能地减少开发人员需要进…

怎么维护好自己的电脑

你的电脑已经成为你工作、学习、娱乐的最佳工具之一&#xff0c;但是如果你不做好电脑维护工作&#xff0c;就可能面临着电脑变慢、蓝屏、崩溃等问题。在这篇文章中&#xff0c;我们将介绍10个电脑维护步骤&#xff0c;让你的电脑更加稳定&#xff01; 为什么需要电脑维护&…

SpringBoot+Vue开发笔记

参考&#xff1a;https://www.bilibili.com/video/BV1nV4y1s7ZN?p1 ----------------------------------------------------------概要总结---------------------------------------------------------- 1、MVC架构&#xff1a; View&#xff1a;与用户交互 Controller&…

Devart dbForge Studio for MySQL Crack

Devart dbForge Studio for MySQL Crack dbForge Studio for MySQL是一个用于MySQL和MariaDB数据库开发、管理和管理的通用GUI工具。IDE允许您通过直观的界面创建和执行查询、开发和调试存储例程、自动化数据库对象管理、分析表数据。MySQL客户端提供了数据和模式比较和同步工具…

全志F1C200S嵌入式驱动开发(应用程序开发)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 我们在开发soc驱动的时候,很多情况下也要验证下当前的驱动功能是否正确。当然除了验证驱动功能之外,我们还要编写业务代码和流程代码。这中间就和各行各业有关了,有的是算法,有…

Ubuntu 虚拟机和主机无法互相复制文字和文件

1.在虚拟机列表中&#xff0c;右键查看是否有安装VMware Tools&#xff0c;如果没有安装点击安装&#xff0c;如果已经安装了&#xff0c;上面显示重现安装VMware Tools&#xff0c;并且为灰色&#xff0c;如图&#xff1a; 2.如果没有安装点击安装&#xff0c;如果已经安装&am…

【2023年电赛国一必备】A题报告模板--可直接使用

任务 图1 任务内容 要求 图2 基本要求内容 图3 发挥部分内容 说明 图4 说明内容 评分标准 图5 评分内容 正文 &#xff08;部分&#xff09; 摘要 本实验旨在设计和制作一个由两个单相逆变器组成的并联系统&#xff0c;用于为电阻负载供电或并入220V电网。采用基于STM…

CASAIM自动化平面度检测设备3D扫描零部件形位公差尺寸测量

平面度是表面形状的度量&#xff0c;指示沿该表面的所有点是否在同一平面中&#xff0c;当两个表面需要连接在一起形成紧密连接时&#xff0c;平面度检测至关重要。 CASAIM自动化平面度检测设备通过搭载领先的激光三维测头和智能检测软件自动获取零部件高质量测量数据&#xf…

单片机】51单片机,TLC2543,驱动程序,读取adc

TLC2543 是一款 12 位精密模数转换器 (ADC)。 1~9、11、12——AIN0&#xff5e;AIN10为模拟输入端&#xff1b; 15——CS 为片选端&#xff1b; 17——DIN 为串行数据输入端&#xff1b;&#xff08;控制字输入端&#xff0c;用于选择转换及输出数据格式&#xff09; 16——…

【Jmeter】压测mysql数据库中间件mycat

目录 背景 环境准备 1、下载Jmeter 2、下载mysql数据库的驱动包 3、要进行测试的数据库 Jmeter配置 1、启动Jmeter图形界面 2、加载mysql驱动包 3、新建一个线程组&#xff0c;然后如下图所示添加 JDBC Connection Configuration 4、配置JDBC Connection Configurati…