【2024 DigiKey 创意大赛】用esp32-s3-lcd-ev-board制作华容道拼图游戏
[复制链接]
本帖最后由 aramy 于 2024-12-17 18:05 编辑
这次参与2024 DigiKey“感知万物,乐享生活”大赛,我选择的板子是“esp32-s3-lcd-ev-board”。这块板子非常豪华地配备了一块480*480的触摸屏,这么大的屏幕,能够非常好滴让单片机与人交互。
- 作品简介
ESP32-S3-LCD-EV-Board 是一款基于 ESP32-S3 芯片的屏幕交互开发板,通过搭配不同类型的 LCD 子板,可以驱动 IIC、SPI、8080 以及 RGB 接口的 LCD 显示屏。同时它还搭载双麦克风阵列,支持语音识别和近/远场语音唤醒,具有触摸屏交互和语音交互功能,满足用户对多种不同分辨率以及接口的触摸屏应用产品的开发需求。本项目是使用ESP32-S3-LCD-EV-Board加上480x480 LCD触摸屏,完成了经典游戏“华容道”拼图游戏。
-
- 系统框图
ESP32-S3-LCD-EV-Board开发板带着一块480X480的触摸屏,很适合与用户以触摸方式进行交互。项目使用了LVGL来进行图形展示和与用户交互。将屏幕分为游戏区和控制区两个部分。
控制区负责控制游戏难度等级,提供了三个按钮。一个按钮为退出按钮,可以退出游戏。另外两个按钮,为调整游戏难度按钮,可以调整游戏难度,一共有16级难度(0~15),调整难度后,都会按当前难度重新初始化游戏界面。
游戏区显示当前各个图块的位置,一共有四种类型的图块,每个图块均可上下左右四个方向移动,用户可以在游戏区通过触摸移动相应的图块进行移动,系统判断当前图块是否符合移动条件,条件符合时就重绘游戏区域,达到移动图块的效果。
当图块符合胜利条件,就胜利,并升级到下一难度等级。
系统开发使用esp-idf,版本选择esp-idf 5.2.1,使用vscode作为开发工具,选择LVGL8.4.0作为UI开发的库。
三、各部分功能说明
- 基础框架:使用官方的例程库作为基础框架。https://github.com/espressif/esp-dev-kits
官方例程库下载下来后,找到esp32-s3-lcd-ev-board下examples里的lvgl_demos项目作为基础项目,在这个基础上叠加自己的功能。
- 现在esp-idf使用了组件方式进行编程,组件无法进行修改。这里将lvgl组件移到本地。建立“components”文件夹,将“managed_components”文件夹下的lvgl__lvgl文件夹移动到“components”文件夹下,并改名为lvgl。
修改main文件夹下的CMakeLists.txt文件。
- set(LV_DEMO_DIR ../components/lvgl/demos)
- file(GLOB_RECURSE LV_DEMOS_SOURCES ${LV_DEMO_DIR}/*.c)
- 在main文件夹下,删除原有的“ui_printer”和“ui_tuner”文件夹,这两个文件夹,项目中用不到。再创建game文件夹,在game下创建文件夹“huarongdao”,用来存放自己的游戏代码文件。最后还需要修改一下CMakeLists.txt文件。
- 修改代码。在main.c主函数中,先引入自己的头函数#include "huarongdao/huaorngdao.h" 。在主函数中保留官方例程的lvgl初始化部分,其余部分删除,添加游戏的调用函数。
- void app_main(void)
- {
- bsp_i2c_init();
- lv_disp_t *disp = bsp_display_start();
- ESP_LOGI(TAG, "Display LVGL demo");
- /**
- * To avoid errors caused by multiple tasks simultaneously accessing LVGL,
- * should acquire a lock before operating on LVGL.
- */
- bsp_display_lock(0);
- huarongdao();
-
- bsp_display_unlock();
- }
-
- 游戏入口函数huarongdao(),在这里开始初始化游戏界面。包括绘制游戏背景图片,绘制按钮,给按钮添加回调事件,游戏负责与用户交互的回调事件为“move_obj_cb”,由这个方法来驱动整个游戏的运作。
- static void move_obj_cb(lv_event_t *e)
- {
- static lv_point_t click_point1, click_point2;
- int movex, movey, direction;
-
- game_obj_type *stage_data = (game_obj_type *)e->user_data;
-
- if (e->code == LV_EVENT_PRESSED)
- {
- lv_indev_get_point(lv_indev_get_act(), &click_point1);
- return;
- }
-
- if (e->code == LV_EVENT_RELEASED)
- {
- lv_indev_get_point(lv_indev_get_act(), &click_point2);
- movex = click_point2.x - click_point1.x;
- movey = click_point2.y - click_point1.y;
-
- if ((movex == 0 && movey == 0) || (movex == movey) || (movex == -movey))
- return;
-
- if ((movex < 0 && movey < 0 && movex > movey) || (movex > 0 && movey < 0 && movex < -movey))
- direction = up;
- if ((movex > 0 && movey < 0 && movex > -movey) || (movex > 0 && movey > 0 && movex > movey))
- direction = right;
- if ((movex < 0 && movey < 0 && movex < movey) || (movex < 0 && movey > 0 && movex < -movey))
- direction = left;
- if ((movex < 0 && movey > 0 && movex > -movey) || (movex > 0 && movey > 0 && movex < movey))
- direction = down;
-
- if (direction == up)
- {
-
- if (stage_data->obj_type == little)
- {
- if (stage_data->y == 0)
- return;
-
- if (game_map[stage_data->y - 1][stage_data->x] == 0)
- {
- game_map[stage_data->y - 1][stage_data->x] = 1;
- game_map[stage_data->y][stage_data->x] = 0;
- stage_data->y--;
- step_count++;
- lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
- lv_obj_set_pos(stage_data->obj, lv_pct((stage_data->x) * 25), lv_pct((stage_data->y) * 20));
- }
- }
-
- if (stage_data->obj_type == big)
- {
- if (stage_data->y == 0)
- return;
-
- if (game_map[stage_data->y - 1][stage_data->x] == 0 && game_map[stage_data->y - 1][(stage_data->x) + 1] == 0)
- {
- game_map[stage_data->y - 1][stage_data->x] = 1;
- game_map[stage_data->y - 1][(stage_data->x) + 1] = 1;
- game_map[stage_data->y + 1][stage_data->x] = 0;
- game_map[stage_data->y + 1][(stage_data->x) + 1] = 0;
- stage_data->y--;
- step_count++;
- lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
- lv_obj_set_pos(stage_data->obj, lv_pct((stage_data->x) * 25), lv_pct((stage_data->y) * 20));
- }
- }
-
- if (stage_data->obj_type == hor)
- {
- if (stage_data->y == 0)
- return;
-
- if (game_map[stage_data->y - 1][stage_data->x] == 0 && game_map[stage_data->y - 1][(stage_data->x) + 1] == 0)
- {
- game_map[stage_data->y - 1][stage_data->x] = 1;
- game_map[stage_data->y - 1][(stage_data->x) + 1] = 1;
- game_map[stage_data->y][stage_data->x] = 0;
- game_map[stage_data->y][(stage_data->x) + 1] = 0;
- stage_data->y--;
- step_count++;
- lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
- lv_obj_set_pos(stage_data->obj, lv_pct((stage_data->x) * 25), lv_pct((stage_data->y) * 20));
- }
- }
-
- if (stage_data->obj_type == ver)
- {
- if (stage_data->y == 0)
- return;
-
- if (game_map[stage_data->y - 1][stage_data->x] == 0)
- {
- game_map[stage_data->y - 1][stage_data->x] = 1;
- game_map[stage_data->y + 1][stage_data->x] = 0;
- stage_data->y--;
- step_count++;
- lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
- lv_obj_set_pos(stage_data->obj, lv_pct((stage_data->x) * 25), lv_pct((stage_data->y) * 20));
- }
- }
- }
-
- if (direction == down)
- {
-
- if (stage_data->obj_type == little)
- {
- if (stage_data->y == 4)
- return;
-
- if (game_map[stage_data->y + 1][stage_data->x] == 0)
- {
- game_map[stage_data->y + 1][stage_data->x] = 1;
- game_map[stage_data->y][stage_data->x] = 0;
- stage_data->y++;
- step_count++;
- lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
- lv_obj_set_pos(stage_data->obj, lv_pct((stage_data->x) * 25), lv_pct((stage_data->y) * 20));
- }
- }
-
- if (stage_data->obj_type == big)
- {
- if (stage_data->y == 3)
- return;
-
- if (game_map[stage_data->y + 2][stage_data->x] == 0 && game_map[stage_data->y + 2][(stage_data->x) + 1] == 0)
- {
- game_map[stage_data->y + 2][stage_data->x] = 1;
- game_map[stage_data->y + 2][(stage_data->x) + 1] = 1;
- game_map[stage_data->y][stage_data->x] = 0;
- game_map[stage_data->y][(stage_data->x) + 1] = 0;
- stage_data->y++;
- step_count++;
- lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
- lv_obj_set_pos(stage_data->obj, lv_pct((stage_data->x) * 25), lv_pct((stage_data->y) * 20));
- }
- }
-
- if (stage_data->obj_type == hor)
- {
- if (stage_data->y == 4)
- return;
-
- if (game_map[stage_data->y + 1][stage_data->x] == 0 && game_map[stage_data->y + 1][(stage_data->x) + 1] == 0)
- {
- game_map[stage_data->y + 1][stage_data->x] = 1;
- game_map[stage_data->y + 1][(stage_data->x) + 1] = 1;
- game_map[stage_data->y][stage_data->x] = 0;
- game_map[stage_data->y][(stage_data->x) + 1] = 0;
- stage_data->y++;
- step_count++;
- lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
- lv_obj_set_pos(stage_data->obj, lv_pct((stage_data->x) * 25), lv_pct((stage_data->y) * 20));
- }
- }
-
- if (stage_data->obj_type == ver)
- {
- if (stage_data->y == 3)
- return;
-
- if (game_map[stage_data->y + 2][stage_data->x] == 0)
- {
- game_map[stage_data->y + 2][stage_data->x] = 1;
- game_map[stage_data->y][stage_data->x] = 0;
- stage_data->y++;
- step_count++;
- lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
- lv_obj_set_pos(stage_data->obj, lv_pct((stage_data->x) * 25), lv_pct((stage_data->y) * 20));
- }
- }
- }
-
- if (direction == left)
- {
-
- if (stage_data->obj_type == little)
- {
- if (stage_data->x == 0)
- return;
-
- if (game_map[stage_data->y][stage_data->x - 1] == 0)
- {
- game_map[stage_data->y][stage_data->x - 1] = 1;
- game_map[stage_data->y][stage_data->x] = 0;
- stage_data->x--;
- step_count++;
- lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
- lv_obj_set_pos(stage_data->obj, lv_pct((stage_data->x) * 25), lv_pct((stage_data->y) * 20));
- }
- }
-
- if (stage_data->obj_type == big)
- {
- if (stage_data->x == 0)
- return;
-
- if (game_map[stage_data->y][stage_data->x - 1] == 0 && game_map[stage_data->y + 1][(stage_data->x) - 1] == 0)
- {
- game_map[stage_data->y][stage_data->x - 1] = 1;
- game_map[stage_data->y + 1][(stage_data->x) - 1] = 1;
- game_map[stage_data->y][stage_data->x + 1] = 0;
- game_map[stage_data->y + 1][(stage_data->x) + 1] = 0;
- stage_data->x--;
- step_count++;
- lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
- lv_obj_set_pos(stage_data->obj, lv_pct((stage_data->x) * 25), lv_pct((stage_data->y) * 20));
- }
- }
-
- if (stage_data->obj_type == hor)
- {
- if (stage_data->x == 0)
- return;
-
- if (game_map[stage_data->y][stage_data->x - 1] == 0)
- {
- game_map[stage_data->y][stage_data->x - 1] = 1;
- game_map[stage_data->y][stage_data->x + 1] = 0;
- stage_data->x--;
- step_count++;
- lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
- lv_obj_set_pos(stage_data->obj, lv_pct((stage_data->x) * 25), lv_pct((stage_data->y) * 20));
- }
- }
-
- if (stage_data->obj_type == ver)
- {
- if (stage_data->x == 0)
- return;
-
- if (game_map[stage_data->y][stage_data->x - 1] == 0 && game_map[stage_data->y + 1][(stage_data->x) - 1] == 0)
- {
- game_map[stage_data->y][stage_data->x - 1] = 1;
- game_map[stage_data->y + 1][stage_data->x - 1] = 1;
- game_map[stage_data->y][stage_data->x] = 0;
- game_map[stage_data->y + 1][stage_data->x] = 0;
- stage_data->x--;
- step_count++;
- lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
- lv_obj_set_pos(stage_data->obj, lv_pct((stage_data->x) * 25), lv_pct((stage_data->y) * 20));
- }
- }
- }
-
- if (direction == right)
- {
-
- if (stage_data->obj_type == little)
- {
- if (stage_data->x == 3)
- return;
-
- if (game_map[stage_data->y][stage_data->x + 1] == 0)
- {
- game_map[stage_data->y][stage_data->x + 1] = 1;
- game_map[stage_data->y][stage_data->x] = 0;
- stage_data->x++;
- step_count++;
- lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
- lv_obj_set_pos(stage_data->obj, lv_pct((stage_data->x) * 25), lv_pct((stage_data->y) * 20));
- }
- }
-
- if (stage_data->obj_type == big)
- {
- if (stage_data->x == 2)
- return;
-
- if (game_map[stage_data->y][stage_data->x + 2] == 0 && game_map[stage_data->y + 1][(stage_data->x) + 2] == 0)
- {
- game_map[stage_data->y][stage_data->x + 2] = 1;
- game_map[stage_data->y + 1][(stage_data->x) + 2] = 1;
- game_map[stage_data->y][stage_data->x] = 0;
- game_map[stage_data->y + 1][(stage_data->x)] = 0;
- stage_data->x++;
- step_count++;
- lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
- lv_obj_set_pos(stage_data->obj, lv_pct((stage_data->x) * 25), lv_pct((stage_data->y) * 20));
- }
- }
-
- if (stage_data->obj_type == hor)
- {
- if (stage_data->x == 2)
- return;
-
- if (game_map[stage_data->y][stage_data->x + 2] == 0)
- {
- game_map[stage_data->y][stage_data->x + 2] = 1;
- game_map[stage_data->y][stage_data->x] = 0;
- stage_data->x++;
- step_count++;
- lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
- lv_obj_set_pos(stage_data->obj, lv_pct((stage_data->x) * 25), lv_pct((stage_data->y) * 20));
- }
- }
-
- if (stage_data->obj_type == ver)
- {
- if (stage_data->x == 3)
- return;
-
- if (game_map[stage_data->y][stage_data->x + 1] == 0 && game_map[stage_data->y + 1][(stage_data->x) + 1] == 0)
- {
- game_map[stage_data->y][stage_data->x + 1] = 1;
- game_map[stage_data->y + 1][stage_data->x + 1] = 1;
- game_map[stage_data->y][stage_data->x] = 0;
- game_map[stage_data->y + 1][stage_data->x] = 0;
- stage_data->x++;
- step_count++;
- lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
- lv_obj_set_pos(stage_data->obj, lv_pct((stage_data->x) * 25), lv_pct((stage_data->y) * 20));
- }
- }
- }
-
- if (stage_data->obj_type == big && stage_data->x == 1 && stage_data->y == 3)
- {
- lv_obj_t *clear_lable = lv_label_create(game_window);
- lv_label_set_text(clear_lable, "STAGE CLEAR");
- lv_obj_set_style_text_color(clear_lable, lv_color_hex(0xffffff), 0);
- lv_obj_center(clear_lable);
- if (current_stage < max_stage - 1)
- {
- current_stage++;
- }
- stage_clear();
- }
- }
- }
四、作品源码
五、作品功能演示视频
播放器加载失败: 未检测到Flash Player,请到 安装
huard
六、项目总结
很伤心,没能完成最初设定的目标!本来想在这个板子上实现机器学习的内容的,实在是能力有限,无法完成。LVGL作为UI实现的工具,功能非常强大,可是总觉着版本有些混乱,不同版本方法差异好大,学习成本有点太高了,但是作为一个连接单片机和人的桥梁还是非常好用的,通过单片机、传感器来感知万物,再用合适的UI展示出来,与人互动。非常喜欢ESP32-S3-LCD-EV-Board开发板,希望能借助这个项目留下这块板子!
|