[LVGL] 在VC_MFC中移植LVGL

前言:

  1. 在MFC中开发LVGL的优点是可以用多个Window界面做辅助扩展

1.本文基于VC2022-MFC单文档框架移植lvgl8

  1. gitee上下载lvgl8.3 源码,并将其文件夹改名为lvgl

lvgl: LVGL 是一个开源图形库,提供您创建具有易于使用的图形元素、漂亮的视觉效果和低内存占用的嵌入式 GUI 所需的一切。 - Gitee.com

步骤:

1.新建一个MFC应用程序,命名为LVGL_MFC [可以自定义]

2.将下载的lvgl源码放到刚新建的LVGL_MFC工程目录下

  1. 设置VS工程的属性:取消C/C++ 预编译头 ;VC++目录包含目录 添加:(MSBuildThisFileDirectory)lvgl;(MSBuildThisFileDirectory)lvgl\src;$(IncludePath)

4.在解决方案资源管理器第一栏,点击"显示所有文件",然后展开lvgl文件夹,在src文件夹右击选择"包含在项目中"。

5.在lvgl目录下将lv_conf_template.h改名为lv_conf.h

6.编译MFC工程,此时应该会出现一个空白的对话框。

7.在lvgl目录下新建文件夹my_porting,文件夹里新建lv_driver_mfc.cpp,并将examples文件夹下的lv_port_disp_template.c 和lv_port_indev_template.c 复制到my_porting目录中,然后两个C文件分别改名为lv_port_disp.c和lv_port_indev.c ;注意将这两个C文件里面开头的#if 0 改为 #if 1 ; 然后在my_porting后击选择"包含在项目中"

8.在lv_port_disp.c中修改如下:

cpp 复制代码
/*********************
 *      DEFINES
 *********************/
#ifndef MY_DISP_HOR_RES
    //#warning Please define or replace the macro MY_DISP_HOR_RES with the actual screen width, default value 320 is used for now.
    #define MY_DISP_HOR_RES   400// Define your window size width
#endif

#ifndef MY_DISP_VER_RES
    //#warning Please define or replace the macro MY_DISP_HOR_RES with the actual screen height, default value 240 is used for now.
    #define MY_DISP_VER_RES    400// Define your window size height
#endif

#define LV_VER_RES_MAX  600


//此函数修改如下
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{

extern void mfc_disp_flush_ex(lv_disp_drv_t* disp_drv, const lv_area_t* area, lv_color_t* color_p);
    if (disp_flush_enabled) {
        mfc_disp_flush_ex(disp_drv, area, color_p);
    }
    return;
}

9.在lv_port_indev.c中修改如下:

cpp 复制代码
static void mouse_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
    extern void mfc_mouse_read_ex(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
    mfc_mouse_read_ex(indev_drv, data);
    return;
}

void lv_port_indev_init(void)
{
    static lv_indev_drv_t indev_drv;
    /*Register a mouse input device*/
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = mouse_read;
    indev_mouse = lv_indev_drv_register(&indev_drv);    
    return;
}
  1. lv_driver_mfc.cpp 内容如下:
cpp 复制代码
#include "afxdialogex.h"
#include"lvgl.h"
#include <src/hal/lv_hal_disp.h>

HWND g_myMainWnd;


#include <io.h>
#include <fcntl.h>
void RedirectIOToConsole()
{
#if 1  //enable=1,在MFC下仍能使用printf在cmd窗口看日志,在初始化中调用即可
	// 分配控制台
	AllocConsole();
	// 重定向标准输入、输出和错误流
	FILE* fp;
	freopen_s(&fp, "CONOUT$", "w", stdout); // 将 stdout 绑定到控制台
	freopen_s(&fp, "CONOUT$", "w", stderr); // 将 stderr 绑定到控制台
	freopen_s(&fp, "CONIN$", "r", stdin);  // 将 stdin 绑定到控制台
#endif
}



extern "C"
{
	void lv_init(void);
	void lv_port_disp_init(void);
	void lv_port_indev_init(void);
	void ui_init(void);
	void mfc_mouse_read_ex(lv_indev_drv_t* indev_drv, lv_indev_data_t* data);
	void mfc_disp_flush_ex(lv_disp_drv_t* disp_drv, const lv_area_t* area, lv_color_t* color_p);

}


void mfc_disp_flush_ex(lv_disp_drv_t* disp_drv, const lv_area_t* area, lv_color_t* color_p)
{
	CWnd* pWnd = CWnd::FromHandle(g_myMainWnd);
	CDC* pDC = pWnd->GetDC(); // 获取设备上下文指针

	if (pDC) {
		// 定义位图信息
		BITMAPINFO bitmapInfo;
		memset(&bitmapInfo, 0, sizeof(bitmapInfo));

		// 设置位图信息头
		bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
		bitmapInfo.bmiHeader.biWidth = area->x2 - area->x1 + 1;
		bitmapInfo.bmiHeader.biHeight = -(area->y2 - area->y1 + 1);  // 使用负值以表示自上而下的位图
		bitmapInfo.bmiHeader.biPlanes = 1;
		bitmapInfo.bmiHeader.biBitCount = LV_COLOR_DEPTH;  // 与LV_COLOR_DEPTH相符
		bitmapInfo.bmiHeader.biCompression = BI_RGB;  // 不压缩

		// 使用StretchDIBits将LVGL显存绘制到窗口,比单点SetPixel刷图效率高很多
		StretchDIBits(pDC->GetSafeHdc(),
			area->x1, area->y1,
			area->x2 - area->x1 + 1,
			area->y2 - area->y1 + 1,
			0, 0,
			area->x2 - area->x1 + 1,
			area->y2 - area->y1 + 1,
			color_p,
			&bitmapInfo,
			DIB_RGB_COLORS,
			SRCCOPY);
	}
	// 通知LVGL刷新完成
	lv_disp_flush_ready(disp_drv);
}

void mfc_mouse_read_ex(lv_indev_drv_t* indev_drv, lv_indev_data_t* data)
{
	POINT pt;
	GetCursorPos(&pt);  // 获取鼠标位置
	ScreenToClient(g_myMainWnd, &pt);  // 转换为窗口内坐标

	data->point.x = pt.x;
	data->point.y = pt.y;
	data->state = (GetAsyncKeyState(VK_LBUTTON) & 0x8000) ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
}


UINT Thread_Lvgl_UiTimer(LPVOID pParam)
{
	while (1)
	{
		lv_tick_inc(5);// 提供lvlg系统定时基准
		Sleep(5);  // 模拟耗时任务
	}
	return 0;
}


UINT Thread_Lvgl_Flushing(LPVOID pParam)
{
	RedirectIOToConsole();//启动日志printf打印窗口
	printf("Start Thread_Lvgl_Flushing.\n");
	int hor = lv_disp_get_hor_res(lv_disp_get_default());
	int ver = lv_disp_get_ver_res(lv_disp_get_default());
	printf("window h=%d,v=%d.\n", hor, ver);

	while (1)
	{
		lv_timer_handler();
		Sleep(50);  // 模拟耗时任务
	}
	return 0;  // 返回值
}


extern "C"
{
	void my_event_cb(lv_event_t* ev)
	{
		static int cnt = 0;
		lv_obj_t* m_parent = lv_scr_act();
		lv_obj_t* m_label = lv_obj_get_child(m_parent, 0);
		lv_label_set_text_fmt(m_label, "LVGL Cnt=%d.", ++cnt);
		printf("UI_Event_Code=%d.Count=%d.\n", ev->code,cnt);
		
	}

	void ui_init()
	{
		lv_obj_t* m_parent = lv_scr_act();
		lv_obj_t* m_label = lv_label_create(m_parent);
		lv_obj_t* m_btn = lv_btn_create(m_parent);

		lv_obj_set_style_bg_opa(m_parent, 0xFF, 0);
		lv_obj_set_style_bg_color(m_parent, lv_color_hex(0xeeaabb), 0);
		lv_label_set_text(m_label, "LVGL on MFC!");
		lv_obj_align(m_label, LV_ALIGN_CENTER, 0, 0);
		
		lv_obj_align(m_btn, LV_ALIGN_DEFAULT, 0, 50);
		lv_obj_add_event_cb(m_btn, my_event_cb, LV_EVENT_CLICKED, NULL);
    }
}


void lvgl_init()
{

	AfxBeginThread(Thread_Lvgl_UiTimer, NULL);
	AfxBeginThread(Thread_Lvgl_Flushing, NULL);

	lv_init();
	lv_port_disp_init();
	lv_port_indev_init();
	ui_init();//do your demo ui layout code
 	
}
  1. 在主界面LVGL_MFCDlg.cpp的OnInitDialog()中添加初始化代码:
cpp 复制代码
	// TODO: 在此添加额外的初始化代码
	extern HWND g_myMainWnd; 
	g_myMainWnd = m_hWnd;
	extern void lvgl_init(void);
	lvgl_init();
  1. 在lv_conf_internal.h 修改 #define LV_COLOR_DEPTH 32 【PC在16bit颜色下会有色差】

13.运行效果:

后续:

完整工程VS_Demo:

lvgl_mfc: 基于lvgl8.3源代码移植到MFC(VC2022)单对话框平台。

相关推荐
刘卜卜&嵌入式2 小时前
C++_设计模式_观察者模式(Observer Pattern)
c++·观察者模式·设计模式
h汉堡2 小时前
C++入门基础
开发语言·c++·学习
XINVRY-FPGA2 小时前
XCZU7EG‑L1FFVC1156I 赛灵思XilinxFPGA ZynqUltraScale+ MPSoC EG
c++·嵌入式硬件·阿里云·fpga开发·云计算·fpga·pcb工艺
_GR3 小时前
2025年蓝桥杯第十六届C&C++大学B组真题及代码
c语言·数据结构·c++·算法·贪心算法·蓝桥杯·动态规划
mahuifa4 小时前
(7)VTK C++开发示例 --- 使用交互器
c++·vtk·cmake·3d开发
光算科技5 小时前
服务器在国外国内用户访问慢会影响谷歌排名吗?
运维·服务器·c++
大炮筒5 小时前
CPPlist初识
数据结构·c++·list
点云SLAM6 小时前
C++中的算术转换、其他隐式类型转换和显示转换详解
c++·static_cast·dynamic_cast·c++中的类型转换·算术类型转换·其他隐式类型转换·显示类型转换
Zfox_7 小时前
Git 进阶之路:高效协作之分支管理
大数据·linux·运维·c++·git·elasticsearch
wenchm7 小时前
细说STM32单片机FreeRTOS任务管理API函数vTaskList()的使用方法
c语言·c++·stm32·单片机·嵌入式硬件