Clion移植LVGL模拟器

LVGL官方提供的模拟器方案是在Win上使用codeblock,但是其推荐的Linux上使用的vscode等一些项目是使用Cmake构建的,而Clion这这个超级好用而且学生免费的IDE又正好是使用Cmake作为构建工具的,理论是可以运行所有Cmake工程的。所以打算尝试将模拟器移植到Clion下。

本文需要准备的环境:

  • Clion:喷气机的C语言IDE,使用学生邮箱免费使用。已配置好Mingw的C语言环境。
  • SDL:一个C语言图形库,lvgl模拟器的依赖。官网,请自行下载SDL2-devel-xxx-mingw版本的压缩包。
  • lvgl:图形库仓库,这里使用v8.2版本。
  • lv_port_pc_eclipse:一个基于Cmake的模拟器仓库,本文的移植对象。
  • lv_drivers:lvgl在pc上的驱动库。

一,SDL库的安装

由于pc的lvgl依赖于SDL库运行,这里需要先在Clion中添加SDL库。

解压SDL

将下载的SDL库解压到一个文件夹,我下载的是SDL2-devel-2.26.5-mingw.zip,将压缩包解压,随便放一个地方。由于我们使用Cmake直接添加库文件,因此不需要设置环境变量之类(网上大都将SDL目录复制到了MinGW目录下,这样虽然方便添加,但会污染MinGW目录,不是很好)。记住x86_64-w64-mingw32目录的位置。

image-20230524235024752

该目录如下:

image-20230525001733713

  • bin包含Dll动态链接库
  • include包含了头文件
  • lib包含了静态库文件

Tip:以下内容仅供学习,如需快速移植请直接到第二节

设置Cmake

新建一个Clion工程,打开CMakeLists.txt,添加以下内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 以下为工程设置,每个人不一样
cmake_minimum_required(VERSION 3.22)
project(test C)

set(CMAKE_C_STANDARD 99)
# 这里设置了一个变量,将上面要记住的目录保存到SDL2_DIR
set(SDL2_DIR E:/C_Lib/SDL2-2.26.5/x86_64-w64-mingw32)
# 添加头文件地址
include_directories(${SDL2_DIR}/include)
# 添加库文件夹地址,添加dll文件夹和lib文件夹
link_directories(${SDL2_DIR}/bin ${SDL2_DIR}/lib)

add_executable(test main.c)
# 设置test编译项目需要连接的库,注意顺序
target_link_libraries(test mingw32 SDL2 SDL2main )

在上面的代码中,需要使用link_directories()同时添加bin和lib这两个库文件搜索路径。

按道理说dll应该只需要在运行时在引用,在编译时不需要,只需要静态库文件,但这里如果不加bin就会在编译时报错,不知道为什么。

使用target_link_libraries()添加项目要使用的库文件,注意添加顺序,被依赖的库放在依赖它的库的后面,被优先编译

这里需要注意,因为SDL2在引用头文件时,将用户的main重定义为了SDLmain进行了接管,因此如果把mingw放最先编译,可能直接找不到WinMain报错。

运行测试代码

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>
#include "SDL2/SDL.h"

int main(int argc, char *argv[]) {
// 初始化SDL
SDL_Init(SDL_INIT_VIDEO);
// 创建SDL窗口指针
SDL_Window *window = SDL_CreateWindow(
"SDL2Test",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
640,
480,
0
);

// 创建渲染器
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_SOFTWARE);
// 设置渲染器
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderClear(renderer);
// 渲染
SDL_RenderPresent(renderer);

// 等待3000ms
SDL_Delay(3000);

// 销毁窗口&退出
SDL_DestroyWindow(window);
SDL_Quit();

return 0;
}


测试效果为出现黑色窗口,几秒后结束。

image-20230525005702346

可能的报错:

1
2
3
4
5
6
7
8
9
10
E:/Code_R/test/main.c: In function 'SDL_main':
E:/Code_R/test/main.c:4:1: error: number of arguments doesn't match prototype
4 | int main() {
| ^~~
In file included from E:/C_Lib/SDL2-2.26.5/x86_64-w64-mingw32/include/SDL2/SDL.h:32,
from E:/Code_R/test/main.c:2:
E:/C_Lib/SDL2-2.26.5/x86_64-w64-mingw32/include/SDL2/SDL_main.h:155:29: error: prototype declaration
155 | extern SDLMAIN_DECLSPEC int SDL_main(int argc, char *argv[]);
| ^~~~~~~~
ninja: build stopped: subcommand failed.

这是由于SDL的接管机制,将main()如下重定义:

1
2
#define main    SDL_main
extern SDLMAIN_DECLSPEC int SDL_main(int argc, char *argv[]);

因此main函数必须为以下形式:

1
int main(int argc, char *argv[]){}

可能的报错:

1
undefined reference to `WinMain'

同样是SDL接管机制导致找不到main函数,这里是因为链接顺序问题导致包含真正main的SDL没有先编译。注意target_link_libraries()的顺序。

可能的报错:

1
2
3
4
5
6
7
8
E:\CLion 2022.1.2\bin\mingw\bin/ld.exe: /Users/valve/release/SDL2/SDL2-2.26.5-source/foo-x64/../src/main/windows/SDL_windows_main.c:73: undefined reference to `SDL_memcpy'
E:\CLion 2022.1.2\bin\mingw\bin/ld.exe: /Users/valve/release/SDL2/SDL2-2.26.5-source/foo-x64/../src/main/windows/SDL_windows_main.c:74: undefined reference to `SDL_free'
E:\CLion 2022.1.2\bin\mingw\bin/ld.exe: /Users/valve/release/SDL2/SDL2-2.26.5-source/foo-x64/../src/main/windows/SDL_windows_main.c:64: undefined reference to `SDL_wcslen'
E:\CLion 2022.1.2\bin\mingw\bin/ld.exe: /Users/valve/release/SDL2/SDL2-2.26.5-source/foo-x64/../src/main/windows/SDL_windows_main.c:64: undefined reference to `SDL_iconv_string'
E:\CLion 2022.1.2\bin\mingw\bin/ld.exe: E:/C_Lib/SDL2-2.26.5/x86_64-w64-mingw32/lib/libSDL2main.a(SDL_windows_main.o): in function `OutOfMemory':
/Users/valve/release/SDL2/SDL2-2.26.5-source/foo-x64/../src/main/windows/SDL_windows_main.c:26: undefined reference to `SDL_ShowSimpleMessageBox'
E:\CLion 2022.1.2\bin\mingw\bin/ld.exe: E:/C_Lib/SDL2-2.26.5/x86_64-w64-mingw32/lib/libSDL2main.a(SDL_windows_main.o): in function `main_getcmdline':
/Users/valve/release/SDL2/SDL2-2.26.5-source/foo-x64/../src/main/windows/SDL_windows_main.c:79: undefined reference to `SDL_SetMainReady'

没有添加bin路径或者link_directories中lib与bin顺序的问题(这个顺序是什么玄学问题)。

可能的问题:

1
cannot find -lSDL2main

link_directories没有添加lib.

可能成功编译但是运行失败,这是因为缺少dll库,将bin文件夹下的dll复制到exe目录下即可。

其他安装方法:

使用SDL库中.cmake文件,使用find_package(SDL2 REQUIRED SDL2)

LVGL项目中使用这种方法。

二,移植lvgl模拟器

将下载的lvgl库和lv_drivers分别放到lv_port_pc_eclipse中的相应文件夹,使用Clion打开项目。

此时应该会报错:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CMake Error at CMakeLists.txt:10 (find_package):
By not providing "FindSDL2.cmake" in CMAKE_MODULE_PATH this project has
asked CMake to find a package configuration file provided by "SDL2", but
CMake did not find one.

Could not find a package configuration file provided by "SDL2" with any of
the following names:

SDL2Config.cmake
sdl2-config.cmake

Add the installation prefix of "SDL2" to CMAKE_PREFIX_PATH or set
"SDL2_DIR" to a directory containing one of the above files. If "SDL2"
provides a separate development package or SDK, be sure it has been
installed.

这是需要设置SDL2中SDL2Config.cmake sdl2-config.cmake文件所在目录。添加:

1
SET(SDL2_DIR E:/C_Lib/SDL2-2.26.5/x86_64-w64-mingw32/lib/cmake/SDL2)

可能报错:

1
undefined reference to `SDL_main'

这是删除了SDL接管导致的,参考main.c的13行,注释掉这个行

1
#define SDL_MAIN_HANDLED /*To fix SDL's "undefined reference to WinMain" issue*/

加了这个修复反而报错了,估计是linux与win的不同导致的。

之后将dll复制到生成的exe相同目录下。

直接运行就行了。

image-20230525011356379

这里涉及到很多关于C语言编译过程的问题,需要补习很多知识。