STM32和emWin必须知道的那些事

news2025/7/27 19:48:41

emWin 是由德国 SEGGER 公司开发,可为图形 LCD 设计提供高级支持,极大简化了 LCD 设计。 为恩智浦ARM 微控制器用户免费提供的 emWin 图形库。

  在国内做嵌入式系统的大部分都使用 emwin, 其简单来说就是一套图形库。

  STemWin是SEGGER公司授权给ST(意法半导体)的。使用ST芯片的用户可以免费使用STemWin。其实不光授权给了ST,还有NXP,Energy Micro等。凡是使用这些芯片厂商生产的处理器都可以免费的使用emWin。但是出于一定的保护措施,使用STemWin的库是不能用在其它芯片厂商的处理器上面的。因为在工程初始化STemWin前要使能CRC校验。如果没有使能,STemWin是启动不起来的。

  STemWin还针对ST的微控制器做了专门的优化,比如在使用ST的F4XX微控制器带FPU的芯片时,STemWin在需要浮点处理的地方专门做了优化。

本文详细介绍了在STM32F1xx开发板上移植STemWin的步骤。

(1)上st.com官网下载STemWin的压缩包:

打开浏览器,输入地址 https://www.st.com/zh/embedded-software/stemwin.html ,打开STemWin官网界面如下:

点击界面上的“获取软件”链接,进入软件下载部分:

点击“Get latest”红色按钮,弹出许可协议按钮:

点击“接受”蓝色按钮,如果登录了,则直接开始下载,如果没有登录,则需要登录 my.st.com 账号,没有账号,注册一个账号再登录即可下载。

下载得到“en.stemwin.zip”压缩文件,解压后得到“STemWin_Library_V1.2.0”文件夹,包含以下4个文件夹:

  • _htmresc //ST的logo图片;

  • Libraries //各种源码库文件,详见下文;

  • Project //各个官方开发板下的程序例程;

  • Utilites //一些工具代码;

  • Release_Notes.html //版本说明连接。

跟移植STemWin有关的是Libraries文件夹。Libraries文件夹打开后如下:

  • CMSIS //存放符合CMSIS标准的文件,包括STM32启动文件、ARM Cortex内核文件和对应外设头文件stm32fxxx.h;

  • STemWinLibrary532 //EmWin的库文件;

  • STM32FXXX_StdPeriph_Driver //存放STM32外设驱动文件,inc目录用于存放外设的头文件,src目录用于存放外设的源文件。

跟移植STemWin有关的是4个文件夹:Config、inc、Lib、和OS。另外Documentation文件夹中有STemWin的参考手册,Simulation文件夹中有关于PC端仿真用到的vs工程文件,Software中有一些工具软件。

(2)拷贝相关的STemWin库文件和文件夹到uVision5的项目目录

要将STemWin库移植到我们自己的STM32程序中,需要将Config、inc、Lib、和OS这4个文件夹拷贝到我们自己的项目文件夹下,例如,在项目根目录下建立一个STemWin目录,然后,将这4个文件夹拷贝进去:

在Lib文件夹中,包含了所有STM32芯片的ARM核的库文件,库文件的命名方式是“STemWin版本_ARM核_是否带OS_开发编译环境.a/lib”。

需要根据自己项目的芯片的ARM核选择不同的,不属于自己项目的芯片的ARM核的库文件可以直接删除。例如,我下面的例子是在STM32F103(CM3核)芯片的板子上,用uVision5(Keil),不带OS的环境下,将STemWin移植到我自己的项目中,所以,我只保留了STemWin532_CM3_Keil.lib文件,其它文件都删除了:

至此,移植需要的STemWin的相关文件都已经拷贝到项目所在目录下,下一步是将需要添加到项目和需要修改的文件添加进项目。

我们可以在项目下建立一个Group,例如,我建立的Group是STemWin,然后,在STemWin中添加库中的6个文件如下:

Config\GUIConf.h

Config\GUIConf.c

Config\GUIDRV_Template.c

Config\LCDConf_FlexColor_Template.c

OS\GUI_X.c //GUI_X.c用于没有使用OS的程序的移植,如果使用了OS,需要改成OS\GUI_X_OS.c

Lib\STemWin532_CM3_Keil.lib

另外,还需要建立2个文件:

一个是空内容的文件LCDConf.h,这个文件放置在代码可以引用的位置,STemWin库会引用该文件,因此,必须创建一个该文件,其内容可以是空;

另一个文件是GUI_X_Touch_Analog.c,这个文件用于STemWin实现触摸屏操控。

至此,创建的Group结构如下:

到这里,要移植的库文件已经全了,后面会说明如何修改这些文件。

(3)测试验证显示屏和触摸屏的驱动函数是否有效

要在STM32嵌入式系统上显示STemWin的界面,除了需要STemWin的库文件,还需要有显示屏的驱动文件和触摸屏的驱动文件。

STM32的显示屏的驱动有2种方式,一种是通过读写寄存器的方式实现LCD显示屏的显示,另一种是通过STM32的DMA2D图形加速功能的LTDC驱动程序实现LCD显示屏的显示。

移植STemWin时,要搞清楚板子的LCD屏使用的是那种驱动方式。不管LCD使用哪种驱动方式,都最好使用统一的接口,方便以后的代码复用。

STemWin种LCD的显示,主要调用2个函数,一个是LCD显示某个点的颜色的函数,一个是读取LCD某个点的颜色值的函数,另外,针对填充色块(FillRect)和显示位图(DrawBitLineXBPP)不同显示屏可能有些不同的优化函数,移植时,可以让STemWin调用这些优化函数。测试显示屏的显示某个点的颜色和读取某个点的颜色的驱动函数是否有效,以及填充色块和显示位图的驱动优化函数(如果有的话)是否有效。

STemWin对触摸屏的驱动函数的调用,只有4个:GUI_TOUCH_X_ActivateX、GUI_TOUCH_X_ActivateY、GUI_TOUCH_X_MeasureX、GUI_TOUCH_X_MeasureY,前2个函数是用于使能触摸屏的轴线,后面2个函数是用于获取触摸点在触摸屏上的轴坐标。测试触摸屏的驱动函数是否可以获取到触摸点在触摸屏上的横纵坐标。

显示屏的显示驱动函数和触摸驱动函数验证通过后,就可以修改拷贝过来的STemWin的库文件了。

(4)修改拷贝过来的STemWin的库文件

(4.1)GUIConf.h文件修改:

//这个文件主要配置一些功能开关,如下:

#define GUI_NUM_LAYERS        1      //UI层数,每层都可设置指定API
#define GUI_OS                (0)    //是否有OS
#define GUI_SUPPORT_TOUCH     (1)   //是否带触摸屏
#define GUI_DEFAULT_FONT      &GUI_Font6x8
#define GUI_SUPPORT_MOUSE     (1)    /* Support a mouse */
#define GUI_WINSUPPORT        (1)    /* Use window manager */
#define GUI_SUPPORT_MEMDEV    (1)    /* Memory device package available */
#define GUI_SUPPORT_DEVICES   (1)    /* Enable use of device pointers */

(4.2)GUIConf.c文件修改:

//本文件定义了GUI_X_Config函数,该函数会被STemWin的GUI_Init()函数调用,我们需要在该函数内为STemWin分配内存:

#define GUI_NUMBYTES    (20 * 1024) //20k内存很小,一般建议使用2M内存以上
//#define GUI_NUMBYTES    (2 * 1024 * 1024) __attribute__((at(0XC0000000))); //如果要在外部SDRAM上申请内存,可以通过at指定内存起始位置
#define GUI_BLOCKSIZE 0X80
U32 aMemory[GUI_NUMBYTES / 4];

void GUI_X_Config(void) {

  U32* aMemory2 = &aMemory[0];

  GUI_ALLOC_AssignMemory((U32*)aMemory2, GUI_NUMBYTES);
  GUI_ALLOC_SetAvBlockSize(GUI_BLOCKSIZE);

  GUI_SetDefaultFont(GUI_FONT_6X8);    
}

(4.3)GUIDRV_Template.c文件修改:

//该文件内定义了STemWin调用显示驱动函数在显示屏上显示的函数,必须修改的函数有2个(_SetPixelIndex和_GetPixelIndex),其他FillRect和DrawBitLineXBPP函数按驱动函数规范修改:

static void _SetPixelIndex(GUI_DEVICE * pDevice, int x, int y, int PixelIndex) {
    LCD_DrawPoint(x, y, PixelIndex);
};
static unsigned int _GetPixelIndex(GUI_DEVICE * pDevice, int x, int y) {
  unsigned int PixelIndex;
    PixelIndex = LCD_ReadPoint(x,y);
  return PixelIndex;
}

(4.4)LCDConf_FlexColor_Template.c文件修改:

//该文件内定义了显示屏的尺寸,以及另外一个要修改的函数时LCD_X_Config(),它会被STemWin的GUI_Init()函数调用

#define XSIZE_PHYS  320  // To be adapted to x-screen size
#define YSIZE_PHYS  240  // To be adapted to y-screen size
#ifndef   VXSIZE_PHYS
  #define VXSIZE_PHYS XSIZE_PHYS
#endif
#ifndef   VYSIZE_PHYS
  #define VYSIZE_PHYS YSIZE_PHYS
#endif
void LCD_X_Config(void) {
  int i;
  #if (NUM_BUFFERS > 1)
  for (i = 0; i < GUI_NUM_LAYERS; i++) 
  {
    GUI_MULTIBUF_ConfigEx(i, NUM_BUFFERS);
  }
  #endif

  //注意,该参数必须是&GUIDRV_Template_API和GUICC_M565(不是GUICC_565)   
  GUI_DEVICE_CreateAndLink(&GUIDRV_Template_API, GUICC_M565, 0, 0);

  //设置显示屏尺寸
  LCD_SetSizeEx(0,lcddev.width,lcddev.height);
  LCD_SetVSizeEx(0,lcddev.width,lcddev.height);

  //设置触摸屏横纵轴取值范围
  GUI_TOUCH_Calibrate(GUI_COORD_X,0,lcddev.width,0,lcddev.width-1);   
  GUI_TOUCH_Calibrate(GUI_COORD_Y,0,lcddev.height,0,lcddev.height-1);
    
  //
    // Setting up VRam address and custom functions for CopyBuffer-, CopyRect- and FillRect operations
    //
    for (i = 0; i < GUI_NUM_LAYERS; i++) 
    {
    _aPendingBuffer[i] = -1;
        
    //
    // Set VRAM address
    //
    LCD_SetVRAMAddrEx(i, (void *)(_aAddr[i]));
        
    //
    // Remember color depth for further operations
    //
    _aBytesPerPixels[i] = LCD_GetBitsPerPixelEx(i) >> 3;
        
    //
    // Set custom functions for several operations
    //
    LCD_SetDevFunc(i, LCD_DEVFUNC_COPYBUFFER, (void(*)(void))_LCD_CopyBuffer);
    LCD_SetDevFunc(i, LCD_DEVFUNC_COPYRECT,   (void(*)(void))_LCD_CopyRect);
        
    //
    // Filling via DMA2D does only work with 16bpp or more
    //
    if (_GetPixelformat(i) <= LTDC_Pixelformat_ARGB4444) 
    {
      LCD_SetDevFunc(i, LCD_DEVFUNC_FILLRECT, (void(*)(void))_LCD_FillRect);
      LCD_SetDevFunc(i, LCD_DEVFUNC_DRAWBMP_8BPP, (void(*)(void))_LCD_DrawBitmap8bpp); 
    }
        
    //
    // Set up drawing routine for 16bpp bitmap using DMA2D
    //
    if (_GetPixelformat(i) == LTDC_Pixelformat_RGB565) 
    {
      LCD_SetDevFunc(i, LCD_DEVFUNC_DRAWBMP_16BPP, (void(*)(void))_LCD_DrawBitmap16bpp);     // Set up drawing routine for 16bpp bitmap using DMA2D. Makes only sense with RGB565
    }

    //
    // Set up drawing routine for 32bpp bitmap using DMA2D
    //
    if (_GetPixelformat(i) == LTDC_Pixelformat_ARGB8888) 
    {
      LCD_SetDevFunc(i, LCD_DEVFUNC_DRAWBMP_32BPP, (void(*)(void))_LCD_DrawBitmap32bpp);     // Set up drawing routine for 16bpp bitmap using DMA2D. Makes only sense with RGB565
    }

    //
    // Set up custom color conversion using DMA2D, works only for direct color modes because of missing LUT for DMA2D destination
    //
    GUICC_M1555I_SetCustColorConv(_Color2IndexBulk_M1555I_DMA2D, _Index2ColorBulk_M1555I_DMA2D); // Set up custom bulk color conversion using DMA2D for ARGB1555
    GUICC_M565_SetCustColorConv  (_Color2IndexBulk_M565_DMA2D,   _Index2ColorBulk_M565_DMA2D);   // Set up custom bulk color conversion using DMA2D for RGB565
    GUICC_M4444I_SetCustColorConv(_Color2IndexBulk_M4444I_DMA2D, _Index2ColorBulk_M4444I_DMA2D); // Set up custom bulk color conversion using DMA2D for ARGB4444
    GUICC_M888_SetCustColorConv  (_Color2IndexBulk_M888_DMA2D,   _Index2ColorBulk_M888_DMA2D);   // Set up custom bulk color conversion using DMA2D for RGB888
    GUICC_M8888I_SetCustColorConv(_Color2IndexBulk_M8888I_DMA2D, _Index2ColorBulk_M8888I_DMA2D); // Set up custom bulk color conversion using DMA2D for ARGB8888

    //
    // Set up custom alpha blending function using DMA2D
    //
    GUI_SetFuncAlphaBlending(_DMA_AlphaBlending); 
        
    //
    // Set up custom function for translating a bitmap palette into index values.
    // Required to load a bitmap palette into DMA2D CLUT in case of a 8bpp indexed bitmap
    //
    GUI_SetFuncGetpPalConvTable(_LCD_GetpPalConvTable);
        
    //
    // Set up a custom function for mixing up single colors using DMA2D
    //
    #if emWin_Optimize 
      GUI_SetFuncMixColors(_DMA_MixColors);
    #endif
        
    //
    // Set up a custom function for mixing up arrays of colors using DMA2D
    //
    GUI_SetFuncMixColorsBulk(_LCD_MixColorsBulk);
  }    
}

(4.5)GUI_X.c文件修改:

//该文件内主要定义了2个时间函数,其中的GUI_X_Delay是开发STemWin程序时常用的延迟函数

volatile GUI_TIMER_TIME OS_TimeMS;

/*********************************************************************
*
*      Timing:
*                 GUI_X_GetTime()
*                 GUI_X_Delay(int)

  Some timing dependent routines require a GetTime
  and delay function. Default time unit (tick), normally is
  1 ms.
*/

GUI_TIMER_TIME GUI_X_GetTime(void) { 
  return OS_TimeMS; 
}

void GUI_X_Delay(int ms) { 
  int tEnd = OS_TimeMS + ms;
  while ((tEnd - OS_TimeMS) > 0);
}

(4.6)GUI_X_Touch_Analog.c文件修改:

//该文件内定义了4个用于STemWin触摸屏函数调用的函数:

void GUI_TOUCH_X_ActivateX(void)
{
}

void GUI_TOUCH_X_ActivateY(void)
{
}

int GUI_TOUCH_X_MeasureX(void)
{
    int ret = ...;
    return ret;
}

int yc = 0;
int GUI_TOUCH_X_MeasureY(void)
{
    int ret = ...;    
    return ret;
}

(5)创建一个窗口测试

(5.1)使用GUIBuilder.exe创建windows框架代码

STemWin的框架代码跟VisualStudio的win32窗体应用程序的框架代码几乎一摸一样,如果对VS的win32窗体应用开发熟悉的话,可以手工编写windows框架代码。

STemWin本身提供了一个工具软件GUIBuilder.exe,通过该软件可以创建windows框架代码文件。

前文提到的STemWin压缩包中,位于Software目录下,如图所示:

双击GUIBuilder.exe后打开界面如下:

如图,创建一个窗体,然后往窗体上拖一个按钮,保存之后可以得到文件WndMainDLG.c,其内容如下,是不是和win32窗体程序很像,我们只需要在_cbDialog函数中编写相关的消息处理函数就可以了:

/*********************************************************************
*                                                                    *
*                SEGGER Microcontroller GmbH & Co. KG                *
*        Solutions for real time microcontroller applications        *
*                                                                    *
**********************************************************************
*                                                                    *
* C-file generated by:                                               *
*                                                                    *
*        GUI_Builder for emWin version 5.32                          *
*        Compiled Oct  8 2015, 11:59:02                              *
*        (c) 2015 Segger Microcontroller GmbH & Co. KG               *
*                                                                    *
**********************************************************************
*                                                                    *
*        Internet: www.segger.com  Support: support@segger.com       *
*                                                                    *
**********************************************************************
*/

// USER START (Optionally insert additional includes)
// USER END

#include "DIALOG.h"

/*********************************************************************
*
*       Defines
*
**********************************************************************
*/
#define ID_WINDOW_0 (GUI_ID_USER + 0x00)
#define ID_BUTTON_0 (GUI_ID_USER + 0x01)


// USER START (Optionally insert additional defines)
// USER END

/*********************************************************************
*
*       Static data
*
**********************************************************************
*/

// USER START (Optionally insert additional static data)
// USER END

/*********************************************************************
*
*       _aDialogCreate
*/
static const GUI_WIDGET_CREATE_INFO _aDialogCreate[] = {
  { WINDOW_CreateIndirect, "Window", ID_WINDOW_0, 0, 0, 320, 240, 0, 0x0, 0 },
  { BUTTON_CreateIndirect, "Button", ID_BUTTON_0, 73, 53, 179, 76, 0, 0x0, 0 },
  // USER START (Optionally insert additional widgets)
  // USER END
};

/*********************************************************************
*
*       Static code
*
**********************************************************************
*/

// USER START (Optionally insert additional static code)
// USER END

/*********************************************************************
*
*       _cbDialog
*/
static void _cbDialog(WM_MESSAGE * pMsg) {
  WM_HWIN hItem;
  int     NCode;
  int     Id;
  // USER START (Optionally insert additional variables)
  // USER END

  switch (pMsg->MsgId) {
  case WM_INIT_DIALOG:
    //
    // Initialization of 'Button'
    //
    hItem = WM_GetDialogItem(pMsg->hWin, ID_BUTTON_0);
    BUTTON_SetText(hItem, "Push Me");
    // USER START (Optionally insert additional code for further widget initialization)
    // USER END
    break;
  case WM_NOTIFY_PARENT:
    Id    = WM_GetId(pMsg->hWinSrc);
    NCode = pMsg->Data.v;
    switch(Id) {
    case ID_BUTTON_0: // Notifications sent by 'Button'
      switch(NCode) {
      case WM_NOTIFICATION_CLICKED:
        // USER START (Optionally insert code for reacting on notification message)
        // USER END
        break;
      case WM_NOTIFICATION_RELEASED:
        // USER START (Optionally insert code for reacting on notification message)
        // USER END
        break;
      // USER START (Optionally insert additional code for further notification handling)
      // USER END
      }
      break;
    // USER START (Optionally insert additional code for further Ids)
    // USER END
    }
    break;
  // USER START (Optionally insert additional message handling)
  // USER END
  default:
    WM_DefaultProc(pMsg);
    break;
  }
}

/*********************************************************************
*
*       Public code
*
**********************************************************************
*/
/*********************************************************************
*
*       CreateWindow
*/
WM_HWIN CreateWindow(void);
WM_HWIN CreateWindow(void) {
  WM_HWIN hWin;

  hWin = GUI_CreateDialogBox(_aDialogCreate, GUI_COUNTOF(_aDialogCreate), _cbDialog, WM_HBKWIN, 0, 0);
  return hWin;
}

// USER START (Optionally insert additional public code)
// USER END

/*************************** End of file ****************************/

(5.2)将windows框架代码拷贝到uVision5项目

将WndMainDLG.c拷贝到uVision5项目中,并创建Widgets.h文件,如图:

将创建窗体的函数封装,写到Widgets.h中,以方便统一使用:

#include "DIALOG.h"

WM_HWIN CreateWindow(void);

(5.3)编写代码显示窗体

//10ms定时器,定时刷新触摸屏触摸状态
void set_touch_detect_timer()
{
  //设置中断
  NVIC_InitTypeDef nvic_tim;
  nvic_tim.NVIC_IRQChannel = TIM2_IRQn;
  nvic_tim.NVIC_IRQChannelPreemptionPriority = 1;
  nvic_tim.NVIC_IRQChannelSubPriority = 2;
  nvic_tim.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&nvic_tim);

  //配置Tim
  TIM_TimeBaseInitTypeDef tim_init;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM7时钟,即内部时钟CK_INT=72M(STM32F103RC的芯片)
  TIM_Cmd(TIM2, DISABLE); //配置前,先关闭计数器
  tim_init.TIM_Prescaler = 72 -1;// 预分频器数值,16位; 计数器的时钟频率CK_CNT等于f CK_PSC /(PSC[15:0]+1)。在每一次更新事件时,PSC的数值被传送到实际的预分频寄存器中。
  tim_init.TIM_Period = 10000;// 自动重装载数值,16位; 即多少个脉冲产生一个更新或中断(1周期)。如果自动重装载数值为0,则计数器停止。
  TIM_TimeBaseInit(TIM2, &tim_init); // 初始化定时器
  TIM_ClearFlag(TIM2, TIM_FLAG_Update);// 清除计数器中断标志位
  TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 开启更新中断(只是开启功能,未工作)
  TIM_Cmd(TIM2, ENABLE);// 是否使能计数器(使能了就会立即开始工作)
}
void TIM2_IRQHandler(void)
{
    GUI_TOUCH_Exec();//必须,10ms刷新一次触摸屏触摸状态
}
int main(void)
{
    Stm32_Clock_Init(360,25,2,8);    //设置时钟,180Mhz
    systick_delay_us(100000);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    
    //初始化内存、显示屏和触摸屏驱动                  
    SDRAM_Init();
    TFTLCD_Init();
    tp_dev.init();
    
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_CRC, ENABLE);
    WM_SetCreateFlags(WM_CF_MEMDEV);    
    GUI_Init();
    GUI_Clear();
    GUI_CURSOR_Show();
    
    //开启定时检测触摸屏的时钟        
    set_touch_detect_timer();

    //创建并显示窗体
    WM_HWIN h = CreateMainWnd();
    while(1) 
    {
        GUI_Exec();//必须,刷新窗体
    }    
}

至此,STemWin移植完毕,可以看到效果如图:

您的打赏是我写作的动力!

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

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

相关文章

HTML DOM 事件监听器

通过JavaScript&#xff0c;我们可以给页面的某些元素添加事件的监听器&#xff0c;当元素触发相应事件的时候监听器就会捕捉到这个事件并执行相应的代码。addEventListener() 方法实例当用户点击按钮时触发监听事件&#xff1a;document.getElementById("myBtn").ad…

Balsamiq Wireframes 安装配置

文章目录Balsamiq Wireframes 安装配置一、简介二、软件特色1、零学习曲线&#xff0c;随时随地提供强大功能2. 专为协作而设计3. 每个人的第一个 UX工具三、Balsamiq Wireframes功能介绍1、工具列2、快速添加工具3、UI库4、帆布5、导航器面板6、键盘快捷键&#xff1a;四、安装…

经纬恒润再传佳讯,斩获大奖

阳春二月&#xff0c;经纬恒润屡传佳讯&#xff0c;凭借产品、研发等多方面的出色表现&#xff0c;再次斩获东风柳汽“优秀供应商”和广汽传祺“科技创新奖”&#xff0c;以实力印证良好口碑&#xff0c;不忘初心&#xff0c;载誉而行&#xff01; 东风柳汽&#xff1a;优秀供…

【信号量机制及应用】

水善利万物而不争&#xff0c;处众人之所恶&#xff0c;故几于道&#x1f4a6; 目录 一、信号量机制 二、信号量的应用 >利用信号量实现进程互斥   >利用信号量实现前驱关系   >利用记录型信号量实现同步 三、例题 四、参考 一、信号量机制 信号量是操作系统提…

现在招个会自动化测试的人是真难呀~你会个锤子的自动化测试

现在招个会自动化测试的人是真难呀~ 前一段时间公司计划要招2个自动化测试到岗&#xff0c;同事面试了十几个来应聘的人&#xff0c;发现一个很奇怪的现象&#xff0c;在面试的时候&#xff0c;如果问的是框架API、脚本编写这些问题&#xff0c;基本上所有人都能对答如流&…

centos6下为Rstudio安装多版本R

之前的R版本太旧,不少包装不上,需要安装新版本的R: R --version R version 3.6.0 (2019-04-26) -- "Planting of a Tree"于是下载最新版R: 因为没有证书,需要加上最后面的参数. wget https://mirrors.tuna.tsinghua.edu.cn/CRAN/src/base/R-4/R-4.2.2.tar.gz --no…

MySQL OCP888题解042-审计日志格式

文章目录1、原题1.1、英文原题1.2、中文翻译1.3、答案2、题目解析2.1、题干解析2.2、选项解析3、知识点3.1、知识点1&#xff1a;审计日志3.1.1、审计日志是什么3.1.2、审计日志格式4、总结1、原题 1.1、英文原题 Consider the MySQL Enterprise Audit plugin. You are check…

好使!NAS中傻瓜式配置反向代理及SSL证书,提升网络安全性!

对于有NAS或者有个人主机的朋友来说&#xff0c;将机器映射到外网是基本操作。 但是一般来说&#xff0c;能直接从外网访问的往往仅有80和443端口。事实上&#xff0c;运营商一般把家庭宽带的这两个端口都封了&#xff0c;所以如果我们想要从外网访问自己家中机器部署的服务&a…

【Ajax】异步通信

一.概述 概念&#xff1a;AJAX(Asynchronous JavaScript And XML)&#xff1a;异步的 JavaScript 和 XML 作用&#xff1a; 与服务器进行数据交换&#xff1a;通过AJAX可以给服务器发送请求&#xff0c;并获取服务器响应的数据 使用了AJAX和服务器进行通信&#xff0c;就可以使…

设计模式-第11章(观察者模式)

观察者模式观察者模式观察者模式的特点老板回来了观察者模式 观察者模式又叫发布订阅模式。 观察者模式定义了一种一对多的依赖关系&#xff0c;让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时&#xff0c;会通知所有观察者对象&#xff0c;使它们能…

nextcloud挂载阿里云oss 过程

1. 情景 &#xff1a; 已经使用docke-compsoe 搭建起来nextcloud 并投入使用 &#xff0c;现在内存不够需要挂一个oss nextcloud挂载阿里云oss大概思路 &#xff1a; 使用阿里官方提供的 ossfs &#xff0c; 将oss挂载到服务器目录中 &#xff0c; 在docker-compose中的 做容…

ArcGIS10.6“License许可启动无响应”解决方法

以下是我尝试过的方法关闭防火墙&#xff08;很必要&#xff09;替换 “Service.txt” 及 "ARCGIS.exe"文件&#xff08;感觉没什么用&#xff09;修改服务设置&#xff08;很必要&#xff09;更改注册表&#xff08;可有&#xff09;更改端口号&#xff08;好像没什…

My RuoYi 微服务

一 快速了解 文档&#xff08;特别详细&#xff09;&#xff1a;介绍 | RuoYi 介绍 技术架构&#xff08;图&#xff09;&#xff1a;介绍 | RuoYi快速了解 技术选型 系统环境主框架持久层视图层内置功能 用户管理&#xff1a;用户是系统操作者&#xff0c;该功能主要完成系…

HBase---HBase基础语法

HBase基础语法 文章目录HBase基础语法基本操作进入 HBase 客户端命令行查看命名空间查看命名空间下的表创建命名空间创建表查看表描述禁用/启用删除表新增列族删除列族更改列族存储版本的限制put 增加数据get 查看数据get条件查询删除指定列族下的指定列删除指定行全表扫描全表…

Java线程池之线程复用原理解析

什么是线程复用 在Java中&#xff0c;我们正常创建线程执行任务&#xff0c;一般都是一条线程绑定一个Runnable执行任务。而Runnable实际只是一个普通接口&#xff0c;真正要执行&#xff0c;则还是利用了Thread类的run方法。这个rurn方法由native本地方法start0进行调用。我们…

2023最新设计模式常见面试题汇总进大厂必备

2023最新设计模式常见面试题汇总1.说一下设计模式&#xff1f;你都知道哪些&#xff1f;2.什么是单例模式&#xff1f;3.什么是简单工厂模式&#xff1f;4.什么是抽象工厂模式&#xff1f;5.什么是观察者模式&#xff1f;1&#xff09;定义观察者&#xff08;消息接收方&#x…

201809-3 CCF 元素选择器 满分题解(超详细注释代码) + 解题思路(超详细)

问题描述 解题思路 根据题意可以知道在查询中可以分为两种情况 第一种是查询一个标签选择器或者id选择器&#xff08;可以称为一级查询&#xff09; 第二种就是存在大于两级的查询&#xff08;可以称为多级查询&#xff09; 显然第一种查询需要存储每一种元素在内容中所有出现…

2023 最新计算机视觉学习路线(入门篇)

计算机视觉是人工智能的一个领域&#xff0c;专注于教计算机解释和理解视觉世界。它涉及使用算法、深度学习模型和其他技术使机器能够识别图像或视频中的对象。计算机视觉可用于各种任务&#xff0c;如面部识别、目标检测、图像分割、运动估计和跟踪等。重要性计算机视觉的重要…

Leetcode DAY 57: 回文子串 and 最长回文子序列

647. 回文子串 题目&#xff1a; Given a string s, return the number of palindromic substrings in it. A string is a palindrome when it reads the same backward as forward. A substring is a contiguous sequence of characters within the string. 1、暴力解法&#…

docker-compose+HAProxy+Keepalived搭建高可用 RabbitMQ 集群

基础环境准备 系统环境&#xff1a;Centos7.6 Docker version&#xff1a; 1.13.1, build 7d71120/1.13.1 Docker Compose version&#xff1a; v2.2.2 三个节点&#xff1a; 10.10.11.79 &#xff08;这一台做rabbitmq集群根节点&#xff09; 10.10.11.80 (这台做haproxyke…