嵌入式 LVGL移植到STM32F4

news2024/11/15 19:31:02

目录

LVGL简介

1、特点

2、LVGL的硬件要求 

3、相关网站 

4、LVGL源码下载 

5、LVGL移植要求 

5.1 移植过程-添加源码 

2、更改接口文件

 3、显示实现

4、添加外部中文字体的方法

5、编译下载后有几种情况 

6、调用显示 

6、GUI-Guider使用

6.1 安装软件

6.2 使用软件 

6.3 移植到STM32 


LVGL简介

  LittlevGL是一个免费的开源图形库,提供了创建嵌入式GUI所需的一切,具有易于使用的图形元素、漂亮的视觉效果和低内存占用。

1、特点

  1. 强大的构建模组:按钮、图表、列表、滑块、图像等
  2. 先进的图形:动画、反锯齿、半透明、平滑滚动
  3. 多样的输入设备:触摸板、鼠标、键盘、编码器等
  4. 多显示器支持:支持同时使用多个TFT或单色显示器
  5. 多语言支持:格式文字编码
  6. 硬件无关:可用于任UTF-8意微控制器或显示器
  7. 可裁剪:用于小内存(64 KB FLASH,16 KB RAM)操作
  8. 外部支持:操作系统、外部存储以及GPU
  9. 单帧缓存:即可实现先进的图形效果
  10. C语言编写:以最大化兼容(C++ 兼容)
  11. 模拟器:无需嵌入式硬件就可以在电脑上开始GUI设计
  12. 文档:在线及离线
  13. 免费开源:基于MIT协议

2、LVGL的硬件要求 

3、相关网站 

  1. LVGL官网:LVGL - Light and Versatile Embedded Graphics Library 
  2. LVGL代码库地址(Simulator、Source、Examples、Esp32、MicroPython):LVGL · GitHub 
  3. LVGL源码下载地址:GitHub - lvgl/lvgl: Embedded graphics library to create beautiful UIs for any MCU, MPU and display type. It's boosted by a professional yet affordable drag and drop UI editor, called SquareLine Studio. 

4、LVGL源码下载 

在源码链接中下载一份源码,LVGL已经更新迭代了很多个版本,这里我们选择8.0.1版本来移植。

 注意:不同版本之间有很大的不同,尽量使用与本文相同的版本,否则不予解释。

下载后得到的目录如下:

 

源码各文件作用

  1. github:github的配置文件,忽略即可。
  2. docs:说明文档。
  3. examples:示例代码。
  4. scripts:配置脚本,Linux平台会用到,Windows忽略即可。
  5. src:源代码。
  6. tests:测试代码。

5、LVGL移植要求 

STM32工程,该工程应具备功能:

LCD显示(必备)

  1. TOUCH触摸(可选)
  2. DMA刷屏(可选)
  3. 内存管理(可选)
  4. 操作系统(可选)

5.1 移植过程-添加源码 

在工程目录下创建LVGL文件夹,

接着, 将LVGL的源码目录(lvgl-8.0.1\src)复制到本工程中的LVGL目录中

 接下来,将LVGL的驱动接口文件目录(lvgl-8.0.1\examples\porting)复制到本工程中的LVGL文件夹中。其中共六个文件,disp为显示接口驱动文件(LCD),fs为文件系统接口驱动文件(FATFS),indev为输入设备接口驱动文件(TOUCH)

接着, 将LVGL源码中(lvgl-8.0.1)的lv_conf_template.h更名为lv_conf.h,随后将其与lvgl.h一同拷贝到本地工程中的LVGL目录下;

 

接着, 打开工程,在工程的目录结构中添加二个文件夹LVGL_SRC和LVGL_PORT,并把源码添加到其中。(可根据需要进行裁剪)。

 

 

添加port文件

 

 不知道就全部都添加。本示例只使用disp这个显示的模板。

 接着,添加源码头文件路径;

 如果工程中没有内存管理,则需要修改启动文件中的堆栈。根据官方推荐我们可以把堆栈修改为4K,假如使用的功能比较多,还需要再适当增大;

 LVGL的源码需要C99的支持,否则编译无法通过;

 next,编译工程

 

 双击错误定位到错误位置发现,该代码包含的头文件路径层级错误,根据目录层级修改。(原来是../../lv_conf,更改为../ lv_conf)

 打开lv_conf文件中的条件编译。lv_conf的条件编译没有打开,去到此文件下打开该文件修改保存即可;

 编译。如果出现以下错误:

 只剩3个错误了,原因是没有添加主题源文件(..\LVGL\src\extra\themes\default)。把下图中的文件添加到工程。

如果出现以下错误:

 不这是因为没有添加控件的源文件,默认所有控件是使能状态的(lv_conf.h),我们把这些源文件(src\extra\widgets目录下)都添加到我们的工程中(如果在添加src文件时已经全部添加则不会出现这些错误)。

 

 最终编译没有错误了。

 移植成功了,但是还显示不了,我们还需要去实现显示的相关接口。

2、更改接口文件

默认lv_port_disp_templ.c和lv_port_disp_templ.h的条件编译是关闭的,我们需要把他打开并修改包含目录层级。

 更改lv_port_disp_templ.c 文件中lv_port_disp_init驱动函数。此函数提供了三种写缓存方式,保留其中一种即可,本示例采用方式二;

方式一:单缓存显示(10行),主控内存较小时选用此方式。

方式二:双缓存显示(两个10行),此方式支持DMA交替传输,缓存区越大,显示效果越好(有条件两个满屏缓存)。

方式三:双满屏缓存显示,相当于有条件的方式二

注释方式一和方式三,更改第二种方式如下:

 

 方式二更改如下:

 

 

更改屏幕大小:

 

 

添加DMA实现代码,并在disp_init函数中调用DMA的初始化函数(如果不使用DMA,disp_flush函数中只需要保留LCD_Color_Fill、lv_disp_flush_ready这两个函数即可)。注意添加需要的头文件。如#include "lcd.h"

  1. 添加一个全局变量static lv_disp_drv_t *disp_drv_p;

 把以下代码添加到lv_port_disp_templ.c,

static void DisPlay_SPI_DMA_Init()
{
    DMA_InitTypeDef  DMA_InitStructure	= {0};
	NVIC_InitTypeDef NVIC_InitStruct	= {0};
	
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); //DMA1时钟使能
    DMA_DeInit(DMA1_Stream7);
    while(DMA_GetCmdStatus(DMA1_Stream7) != DISABLE) {}  //等待DMA可配置

    /* 配置 DMA Stream */
    DMA_InitStructure.DMA_Channel 				= DMA_Channel_0;  				//通道选择
    DMA_InitStructure.DMA_PeripheralBaseAddr 	= (unsigned int)&SPI3->DR; 		//DMA外设地址
    DMA_InitStructure.DMA_Memory0BaseAddr 		= (unsigned int)buf_2_1;		//DMA 存储器0地址
    DMA_InitStructure.DMA_DIR 					= DMA_DIR_MemoryToPeripheral;	//存储器到外设模式
    DMA_InitStructure.DMA_BufferSize 			= sizeof(buf_2_2);				//数据传输量
    DMA_InitStructure.DMA_PeripheralInc 		= DMA_PeripheralInc_Disable;	//外设非增量模式
    DMA_InitStructure.DMA_MemoryInc 			= DMA_MemoryInc_Enable;			//存储器增量模式
    DMA_InitStructure.DMA_PeripheralDataSize 	= DMA_PeripheralDataSize_Byte;	//外设数据长度:8位
    DMA_InitStructure.DMA_MemoryDataSize 		= DMA_MemoryDataSize_Byte;		//存储器数据长度:8位
    DMA_InitStructure.DMA_Mode 					= DMA_Mode_Normal;				//使用普通模式
    DMA_InitStructure.DMA_Priority 				= DMA_Priority_High;			//中等优先级
    DMA_InitStructure.DMA_FIFOMode 				= DMA_FIFOMode_Disable;			//不使用fifo
    DMA_InitStructure.DMA_FIFOThreshold 		= DMA_FIFOThreshold_Full;		//fifo全容量
    DMA_InitStructure.DMA_MemoryBurst 			= DMA_MemoryBurst_Single;		//存储器突发单次传输
    DMA_InitStructure.DMA_PeripheralBurst 		= DMA_PeripheralBurst_Single;	//外设突发单次传输
    DMA_Init(DMA1_Stream7, &DMA_InitStructure);									//初始化DMA Stream
        
    SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, ENABLE); // SPI3使能DMA发送
    
	NVIC_InitStruct.NVIC_IRQChannel = DMA1_Stream7_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
	NVIC_Init(&NVIC_InitStruct);
	
    DMA_ITConfig(DMA1_Stream7, DMA_IT_TC, ENABLE);
	
	DMA_Cmd(DMA1_Stream7, DISABLE);
}


/*********************************************************************************************************
* 函 数 名 : DisPlay_SPI_DMA_Enable
* 功能说明 : 配置DMA并启动一次传输
* 形    参 : buf:需要搬运的数据的指针;size:搬运的数据量
* 返 回 值 : 无
* 备    注 : 无
*********************************************************************************************************/ 
void DisPlay_SPI_DMA_Enable(void *buf, unsigned int size)
{
	DMA1_Stream7->CR &= ~(0x01);
	while((DMA1_Stream7->CR&0X1)){}
    DMA1_Stream7->M0AR = (unsigned int)buf;
 DMA1_Stream7->NDTR = size;
	DMA1_Stream7->CR |= (0x01);	
}
/*********************************************************************************************************
* 函 数 名 : DMA1_Stream7_IRQHandler
* 功能说明 : DMA1_Stream7发送完成中断
* 形    参 : 无
* 返 回 值 : 无
* 备    注 : 无
*********************************************************************************************************/ 
void DMA1_Stream7_IRQHandler(void)
{
    if(DMA_GetITStatus(DMA1_Stream7, DMA_IT_TCIF7) != RESET)
	//if(DMA1->HISR & (1<<27))
    {
		DMA_ClearITPendingBit(DMA1_Stream7, DMA_IT_TCIF7);
		//DMA1->HIFCR |= (1<<27);
		LCD_CS = 1;
		SPI3->DR;	
     lv_disp_flush_ready(disp_drv_p);	/* tell lvgl that flushing is done */
    }
}

在disp_init函数中调用DMA的初始化函数。

 修改disp_flush函数

/* Flush the content of the internal buffer the specific area on the display
 * You can use DMA or any hardware acceleration to do this operation in the background but
 * 'lv_disp_flush_ready()' has to be called when finished. */
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
    /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/

//    int32_t x;
//    int32_t y;
//    for(y = area->y1; y <= area->y2; y++) {
//        for(x = area->x1; x <= area->x2; x++) {
//            /* Put a pixel to the display. For example: */
//            /* put_px(x, y, *color_p)*/
//            color_p++;
//        }
//    }
    unsigned int size = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1) * 2;

		disp_drv_p = disp_drv;

		LCD_Address_Set(area->x1, area->y1, area->x2, area->y2+1);	
		LCD_CS = 0;
		DisPlay_SPI_DMA_Enable(color_p, size);

	/*不使用DMA的显示方法*/
//	LCD_Color_Fill(area->x1, area->y1, area->x2-area->x1, area->y2-area->y1+1, (unsigned short *)color_p);
 /* IMPORTANT!!!
     * Inform the graphics library that you are ready with the flushing*/
//    lv_disp_flush_ready(disp_drv);
}

最后编译;

还有一个错误;

 

 最终编译无误。

lv_port_disp_templ.c 文件最终更改后如下;

#if 1
#include "lv_port_disp_template.h"
#include "lcd.h"


#define MY_DISP_HOR_RES 240  //屏的大小
#define MY_DISP_VER_RES 240
static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10];                        /*A buffer for 10 rows*/
static lv_color_t buf_2_2[MY_DISP_VER_RES * 10];                        /*An other buffer for 10 rows*/

static lv_disp_drv_t *disp_drv_p;

static void disp_init(void);

static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p);

/*********************************************************************************************************
* 函 数 名 : DisPlay_SPI_DMA_Init
* 功能说明 : SPI3 DMA1初始化
* 形    参 : 无
* 返 回 值 : 无
* 备    注 : DMA1_Stream7搬运显示数据到SPI3的DR寄存器
*********************************************************************************************************/ 
static void DisPlay_SPI_DMA_Init()
{
    DMA_InitTypeDef  DMA_InitStructure	= {0};
	NVIC_InitTypeDef NVIC_InitStruct	= {0};
	
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); //DMA1时钟使能
    DMA_DeInit(DMA1_Stream7);
    while(DMA_GetCmdStatus(DMA1_Stream7) != DISABLE) {}  //等待DMA可配置

    /* 配置 DMA Stream */
    DMA_InitStructure.DMA_Channel 				= DMA_Channel_0;  				//通道选择
    DMA_InitStructure.DMA_PeripheralBaseAddr 	= (unsigned int)&SPI3->DR; 		//DMA外设地址
 DMA_InitStructure.DMA_Memory0BaseAddr 		= (unsigned int)buf_2_1;		//DMA 存储器0地址
    DMA_InitStructure.DMA_DIR 					= DMA_DIR_MemoryToPeripheral;	//存储器到外设模式
    DMA_InitStructure.DMA_BufferSize 			= sizeof(buf_2_2);				//数据传输量
    DMA_InitStructure.DMA_PeripheralInc 		= DMA_PeripheralInc_Disable;	//外设非增量模式
    DMA_InitStructure.DMA_MemoryInc 			= DMA_MemoryInc_Enable;			//存储器增量模式
    DMA_InitStructure.DMA_PeripheralDataSize 	= DMA_PeripheralDataSize_Byte;	//外设数据长度:8位
    DMA_InitStructure.DMA_MemoryDataSize 		= DMA_MemoryDataSize_Byte;		//存储器数据长度:8位
    DMA_InitStructure.DMA_Mode 					= DMA_Mode_Normal;				//使用普通模式
    DMA_InitStructure.DMA_Priority 				= DMA_Priority_High;			//中等优先级
    DMA_InitStructure.DMA_FIFOMode 				= DMA_FIFOMode_Disable;			//不使用fifo
    DMA_InitStructure.DMA_FIFOThreshold 		= DMA_FIFOThreshold_Full;		//fifo全容量
    DMA_InitStructure.DMA_MemoryBurst 			= DMA_MemoryBurst_Single;		//存储器突发单次传输
    DMA_InitStructure.DMA_PeripheralBurst 		= DMA_PeripheralBurst_Single;	//外设突发单次传输
    DMA_Init(DMA1_Stream7, &DMA_InitStructure);									//初始化DMA Stream
        
    SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, ENABLE); // SPI3使能DMA发送
    
	NVIC_InitStruct.NVIC_IRQChannel = DMA1_Stream7_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
	NVIC_Init(&NVIC_InitStruct);
	
    DMA_ITConfig(DMA1_Stream7, DMA_IT_TC, ENABLE);
	
	DMA_Cmd(DMA1_Stream7, DISABLE);
}
/*********************************************************************************************************
* 函 数 名 : DisPlay_SPI_DMA_Enable
* 功能说明 : 配置DMA并启动一次传输
* 形    参 : buf:需要搬运的数据的指针;size:搬运的数据量
* 返 回 值 : 无
* 备    注 : 无
*********************************************************************************************************/ 
void DisPlay_SPI_DMA_Enable(void *buf, unsigned int size)
{
	DMA1_Stream7->CR &= ~(0x01);
	while((DMA1_Stream7->CR&0X1)){}
    DMA1_Stream7->M0AR = (unsigned int)buf;
    DMA1_Stream7->NDTR = size;
	DMA1_Stream7->CR |= (0x01);	
}
/*********************************************************************************************************
* 函 数 名 : DMA1_Stream7_IRQHandler
* 功能说明 : DMA1_Stream7发送完成中断
* 形    参 : 无
* 返 回 值 : 无
* 备    注 : 无
*********************************************************************************************************/ 
void DMA1_Stream7_IRQHandler(void)
{
    if(DMA_GetITStatus(DMA1_Stream7, DMA_IT_TCIF7) != RESET)
	//if(DMA1->HISR & (1<<27))
    {
		DMA_ClearITPendingBit(DMA1_Stream7, DMA_IT_TCIF7);
		//DMA1->HIFCR |= (1<<27);
		LCD_CS = 1;
		SPI3->DR;	
     lv_disp_flush_ready(disp_drv_p);	/* tell lvgl that flushing is done */
    }
}




void lv_port_disp_init(void)
{
    disp_init();
    static lv_disp_draw_buf_t draw_buf_dsc_2;
    lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, MY_DISP_HOR_RES * 10);   /*Initialize the display buffer*/

    static lv_disp_drv_t disp_drv;                         /*Descriptor of a display driver*/
    lv_disp_drv_init(&disp_drv);                    /*Basic initialization*/
	
	disp_drv.hor_res = MY_DISP_HOR_RES;
    disp_drv.ver_res = MY_DISP_VER_RES;

    disp_drv.flush_cb = disp_flush;
    disp_drv.draw_buf = &draw_buf_dsc_2;
    lv_disp_drv_register(&disp_drv);
}

static void disp_init(void)
{
    /*You code here*/
	DisPlay_SPI_DMA_Init();
}

static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{

    unsigned int size = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1) * 2;
	disp_drv_p = disp_drv;
	LCD_Address_Set(area->x1, area->y1, area->x2, area->y2+1);	
	LCD_CS = 0;
	DisPlay_SPI_DMA_Enable(color_p, size);
}

#else 
typedef int keep_pedantic_happy;
#endif

根据需求修改lv_conf.h的宏定义;

  1. LV_COLOR_DEPTH:屏幕的色彩深度,支持1bit、8bit、16bit、32bit。
  2. LV_COLOR_16_SWAP:字节交换,DMA刷屏的时候需要置1。
  3. LV_MEM_SIZE:GUI可支配的内存空间,根据使用的功能调节。
  4. LV_FONT_MONTSERRAT_*:字体大小,太小会很模糊。

 

 

 如果需要更改默认字体大小,则把相应的宏设置为1.

 

 3、显示实现

配置一个定时器为LVGL提供1ms的时钟心跳,该定时器的中断服务函数中调用lv_tick_inc(1);即可

 主程序中调用lv_task_handler();函数处理事件

 

编写显示程序

#include "lvgl.h"
#include "lv_port_disp_template.h"
void Lvgl_Lable_Demo(void)
{
  _obj_t *scr = lv_scr_act();
lv_obj_t * label1 = lv_label_create(scr);
    lv_label_set_long_mode(label1, LV_LABEL_LONG_WRAP);    
    lv_label_set_recolor(label1, true);                      
    lv_label_set_text(label1, "#0000ff Re-color# #ff00ff words# #ff0000 of a# label, align the lines to the center "
                              "and wrap long text automatically.");	
    lv_obj_set_width(label1, 150);  
    lv_obj_set_style_text_align(label1, LV_TEXT_ALIGN_CENTER, 0);	
    lv_obj_align(label1, LV_ALIGN_CENTER, 0, -40);	


    lv_obj_t * label2 = lv_label_create(scr);
    lv_label_set_long_mode(label2, LV_LABEL_LONG_SCROLL_CIRCULAR);     
    lv_obj_set_width(label2, 150);	
    lv_label_set_text(label2, "It is a circularly scrolling text. ");	
    lv_obj_align(label2, LV_ALIGN_CENTER, 0, 40);	
}

int main()
{
…..
Lcd_Init();
	lv_init();						// 初始化lvgl
	lv_port_disp_init();  			// 显示初始化
	Lvgl_Lable_Demo();
	….
	while(1)
	{
		lv_task_handler();
		Delay_Ms(1);
	}
}

4、添加外部中文字体的方法

打开字库生成工具,选择字体,选择TTF字体,加入常用字体,清除重复。目前抗齿距只能使用4,版本选择6.0以上,XBF字体,外部BIN。点击保存可以存出C文件,点击开始转换即可转换出BIN文件。

 然后点击加入常用汉字à保存à开始转换;

 得到以下两个文件;

 myFont.bin烧写到自己的SPI FLASH相应的地址。烧写过程你懂的。

myFont.c添加到自己的LVGL工程中并添加自定义的字体

lvgl.h处添加用户字体声明;

 将myFont.c中lv_font_t myFont结构体类型加上const修饰,其他需要修改的代码如。

 

5、编译下载后有几种情况 

(1)乱码:抗齿距不是4,多试几种;FLASH读写异常,先测试是否能正常读写。

(2)只有英文:显示中文的C文件编码不是UTF-8,使用软件修改为UTF-8编码后编译报错,在C/C++选项卡中的Misc Controls选项中添加--locale=english

 

6、调用显示 

void Lvgl_Lable_Demo(void)
{
 lv_obj_t *scr = lv_scr_act();
lv_obj_t * label1 = lv_label_create(scr);
    lv_label_set_long_mode(label1, LV_LABEL_LONG_WRAP);    
    lv_label_set_recolor(label1, true);                      
    lv_label_set_text(label1, "#0000ff Re-color# #ff00ff words# #ff0000 of a# label, align the lines to the center "
                              "and wrap long text automatically");	
    lv_obj_set_width(label1, 150);  
    lv_obj_set_style_text_align(label1, LV_TEXT_ALIGN_CENTER, 0);	
    lv_obj_align(label1, LV_ALIGN_CENTER, 0, -40);	


    lv_obj_t * label2 = lv_label_create(scr);
    lv_label_set_long_mode(label2, LV_LABEL_LONG_SCROLL_CIRCULAR);     
    lv_obj_set_width(label2, 150);	
    lv_label_set_text(label2, "It is a circularly scrolling text.中国");	
    lv_obj_align(label2, LV_ALIGN_CENTER, 0, 40);	
}

6、GUI-Guider使用

GUI Guider是恩智浦为LVGL开发了一个上位机GUI设计工具,可以通过拖放控件的方式设计LVGL GUI页面,加速GUI的设计。

设计完成的GUI页面可以在PC上仿真运行,确认设计完毕之后可以生成C代码,再整合到MCU项目中。

GUI Guider(Version: 1.3.0-GA)的主要特征:

  1. 支持Windows 10和Ubuntu 20.04。
  2. 支持中文、英文。
  3. 兼容LVGL V7和LVGL V8版本。
  4. 支持拖放的所见即所得(WYSIWYG)用户界面设计。
  5. 多种字体支持及第三方字体导入。
  6. 可定制的中文字符范围。
  7. 小部件对齐方式:左、中、右。
  8. 自动产生LVGL C语言源代码。
  9. 支持默认样式和自定义样式。
  10. 演示应用程序集成。
  11. 实时日志显示。
  12. 集成上位机仿真器。

GUI-Guider会依赖JDK,因此需要安装jdk.

6.1 安装软件

GUI-Guider会依赖JDK,因此需要安装jdk.

6.2 使用软件 

1、打开GUI-Guider-1.3.0-GA软件;

 2、创建工程;

3、创建完成进入设计界面;

 

 左侧是组件区,中间是设计区,右边是控件地属性设置区。按照自己需要设计GUI页面,

 上图事件功能是按下按钮是把按钮的背景改为黑色。

运行模拟器预览一下效果

 

 没问题之后就可以生成代码(记得先保存工程),在Guider中也可以在代码窗口查看生成的代码.

 生成的代码在Guider工程目录的generated和custom文件夹下

 

6.3 移植到STM32 

把generated和custom文件夹工程整个复制到我们的keil工程目录中:

 

添加generated和custom文件夹相关源文件;

 

 

 

 

 

添加头文件路径

 编写测试代码

 更改错误:一般都是头文件路径不对,哪里不对改哪里。比如

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

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

相关文章

Kakfa详解(一)

kafka使用场景 canal同步mysqlelk日志系统业务系统Topic kafka基础概念 Producer: 消息生产者&#xff0c;向kafka发送消息Consumer: 从kafka中拉取消息消费的客户端Consumer Group: 消费者组&#xff0c;消费者组是多个消费者的集合。消费者组之间互不影响&#xff0c;所有…

Wireshark+Go捕获本地TCP通信

初学计网&#xff0c;使用Wireshark观察本地端口间TCP通信过程。 目录 步骤1&#xff1a; 步骤2&#xff1a; 步骤3&#xff1a; 步骤1&#xff1a; 使用go语言搭建本地客户端与服务器TCP通信&#xff0c;测试完成后在步骤2先运行服务器&#xff0c;再运行客户端。 服务器…

C语言查漏补缺(进阶)volatile、__attribute__、void*、地址对齐、$$Super$main

最近在学习RT-Thread&#xff0c;在看其源码的时候发现了许多自己不太了解的C语言知识点&#xff0c;在此查漏补缺一下。 1. 关键字 volatile volatile是C90新增关键字&#xff0c;volatile的的中文意思是adj.易变的&#xff1b;无定性的&#xff1b;无常性的&#xff1b;可…

如何使用FarsightAD在活动目录域中检测攻击者部署的持久化机制

关于FarsightAD FarsightAD是一款功能强大的PowerShell脚本&#xff0c;该工具可以帮助广大研究人员在活动目录域遭受到渗透攻击之后&#xff0c;检测到由攻击者部署的持久化机制。 该脚本能够生成并导出各种对象及其属性的CSV/JSON文件&#xff0c;并附带从元数据副本中获取…

Python|每日一练|递归|数学|数组|动态规划|树|深度优先搜索|单选记录:排列序列|三角形最小路径和|求根节点到叶节点数字之和

1、排列序列&#xff08;递归&#xff0c;数学&#xff09; 给出集合 [1,2,3,...,n]&#xff0c;其所有元素共有 n! 种排列。 按大小顺序列出所有排列情况&#xff0c;并一一标记&#xff0c;当 n 3 时, 所有排列如下&#xff1a; "123""132""213…

webpack基本使用和开发环境配置

目录 1 webpack 基本使用 01 webpack 简介 02 webpack 初体验 2 webpack开发环境配置 03 打包样式资源 04 打包html资源 05 打包图片资源 06 打包其他资源&#xff08;以打包icon为例&#xff09; 07 devServer 08.开发环境配置 1 webpack 基本使用 由于笔记文档没有…

批量下载Landsat遥感影像的方法

本文介绍在USGS网站批量下载Landsat系列遥感影像的方法。首先打开EarthExplorer的官网&#xff0c;首先完成注册与登录。接下来点击左侧“Search Criteria”&#xff0c;首先选择研究区域。研究区域的划定有多种方法&#xff0c;可以依据地理名称选定研究区域&#xff0c;也可以…

klipper使用webcam设置多个摄像头方式

一、前言 使用klipper设置多个摄像头&#xff0c;折腾了好些天&#xff0c;网上资料很少&#xff0c;这里写一个帖子记录一下 二、环境 参考链接&#xff1a;https://www.cnblogs.com/sjqlwy/p/klipper_webcam.html 我的klipper安装在香橙派上面&#xff0c;系统是debian&a…

这一次,彻底入门前端测试,覆盖单元测试、组件测试(2.4w 字)

前端测试一直是前端工程化中很重要的话题&#xff0c;但是很多人往往对测试产生误解&#xff0c;认为测试不仅没有什么用而且还浪费时间&#xff0c;或者测试应该让测试人员来做&#xff0c;自己应该专注于开发。所以&#xff0c;文章开头会先从"软件工程语境下的软件测试…

【运筹优化】剩余空间法求解带顺序约束的二维矩形装箱问题 + Java代码实现

文章目录一、带顺序约束的二维矩形装箱问题二、剩余空间法三、完整代码实现3.1 Instance 实例类3.2 Item 物品类3.3 PlaceItem 已放置物品类3.4 Solution 结果类3.5 RSPackingWithWeight 剩余空间算法类3.6 Run 运行类3.7 测试案例3.8 ReadDataUtil 数据读取类3.9 运行结果展示…

Spring boot + mybatis-plus 遇到 数据库字段 创建不规范 大驼峰 下划线 导致前端传参数 后端收不到参数 解决方案

最近使用springboot 连接了一个 sqlserver 数据库 由于数据库年数久远 &#xff0c;建表字段不规范 大驼峰 下划线的字段名都有 但是 java 中 Spring boot mybatis-plus 又严格按照小驼峰 格式 生成实体类 如果不是小驼峰格式 Data 注解 get set 方法 在前端请求参数 使用这个…

如何评估模糊测试工具-unibench的使用

unibench是一个用来评估模糊测试工具的benchmark。这个benchmark集成了20多个常用的测试程序&#xff0c;以及许多模糊测试工具。 这篇文章&#xff08;https://zhuanlan.zhihu.com/p/421124258&#xff09;对unibench进行了简单的介绍&#xff0c;本文就不再赘诉&#xff0c;…

设计模式-第6章(工厂模式)

工厂模式简单工厂实现工厂模式实现简单工厂 VS 工厂方法商场收银程序再再升级&#xff08;简单工厂策略装饰工厂方法&#xff09;工厂方法模式总结简单工厂实现 在简单工厂类中&#xff0c;通过不同的运算符&#xff0c;创建具体的运算类。 public class OperationFactory {pu…

CMMI流程规范—实现与测试

一、概述实现与测试&#xff08;Implementation and Test, IT&#xff09;的目的是依据系统设计文档&#xff0c;编写并测试整个系统的代码。在本规范中&#xff0c;实现与测试是“编程、代码审查、单元测试、集成测试、缺陷管理与改错”的综合表述。实现与测试过程域是SPP模型…

从 AI 绘画到 ChatGPT,聊聊生成式 AI

我们小时候经常有幻想&#xff0c;未来不用再去上班了&#xff0c;在工厂工作的都是机器人。在家也不用打扫卫生&#xff0c;机器人可以包揽一切。不知不觉间&#xff0c;我们小时候的幻想已经慢慢变成现实&#xff0c;工厂里有了多种型号的机械臂&#xff0c;代替了部分流水线…

Vue3中watch的value问题

目录前言一&#xff0c;ref和reactive的简单复习1.ref函数1.2 reactive函数1.3 用ref定义对象类型数据不用reactive二&#xff0c;watch的value问题2.1 ref2.1.1 普通类型数据2.1.2 对象类型数据2.1.3 另一种方式2.2 reactive三&#xff0c;总结后记前言 在Vue3中&#xff0c;…

论文投稿指南——中文核心期刊推荐(中国文学作品)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…

微信小程序通过 node 连接 mysql——方法,简要原理,及一些常见问题

前言 博主自己在22年夏天根据课程要求做了一个小程序连接阿里云服务器的案例&#xff0c;在最近又碰到了相应的需求。 原参考文章&#xff1a;微信小程序 Node连接本地MYSQL_微信小程序nodejs连接数据库_JJJenny0607的博客-CSDN博客 ,还请多多支持原作者&#xff01; 第二次…

vue2 @hook 的解析与妙用

目录前言几种用法用法一 将放在多个生命周期的逻辑&#xff0c;统一到一个生命周期中用法二 监听子组件生命周期运行的情况运用场景场景一 许多时候&#xff0c;我们不得不在不同的生命周期中执行某些逻辑&#xff0c;并且这些逻辑会用到一些通用的变量&#xff0c;这些通用变量…

nginx日志服务之敏感信息脱敏

1. 创建实验资源 开始实验之前&#xff0c;您需要先创建实验相关资源。 日志服务之敏感信息脱敏与审计 2. 创建原始数据 本步骤将指导您如何创建NGINX模拟数据。 双击打开虚拟桌面的Firefox ESR浏览器。 在RAM用户登录框中单击下一步&#xff0c;并复制粘贴页面左上角的子…