1183|8

95

帖子

1

TA的资源

一粒金砂(高级)

楼主
 

【2024 DigiKey 创意大赛】用esp32-s3-lcd-ev-board制作华容道拼图游戏 [复制链接]

  本帖最后由 aramy 于 2024-12-17 18:05 编辑

这次参与2024 DigiKey“感知万物,乐享生活”大赛,我选择的板子是“esp32-s3-lcd-ev-board”。这块板子非常豪华地配备了一块480*480的触摸屏,这么大的屏幕,能够非常好滴让单片机与人交互。


  1. 作品简介
    ESP32-S3-LCD-EV-Board 是一款基于 ESP32-S3 芯片的屏幕交互开发板,通过搭配不同类型的 LCD 子板,可以驱动 IIC、SPI、8080 以及 RGB 接口的 LCD 显示屏。同时它还搭载双麦克风阵列,支持语音识别和近/远场语音唤醒,具有触摸屏交互和语音交互功能,满足用户对多种不同分辨率以及接口的触摸屏应用产品的开发需求。本项目是使用ESP32-S3-LCD-EV-Board加上480x480 LCD触摸屏,完成了经典游戏“华容道”拼图游戏。

  2. 系统框图
ESP32-S3-LCD-EV-Board开发板带着一块480X480的触摸屏,很适合与用户以触摸方式进行交互。项目使用了LVGL来进行图形展示和与用户交互。将屏幕分为游戏区和控制区两个部分。
控制区负责控制游戏难度等级,提供了三个按钮。一个按钮为退出按钮,可以退出游戏。另外两个按钮,为调整游戏难度按钮,可以调整游戏难度,一共有16级难度(0~15),调整难度后,都会按当前难度重新初始化游戏界面。
游戏区显示当前各个图块的位置,一共有四种类型的图块,每个图块均可上下左右四个方向移动,用户可以在游戏区通过触摸移动相应的图块进行移动,系统判断当前图块是否符合移动条件,条件符合时就重绘游戏区域,达到移动图块的效果。
当图块符合胜利条件,就胜利,并升级到下一难度等级。
系统开发使用esp-idf,版本选择esp-idf 5.2.1,使用vscode作为开发工具,选择LVGL8.4.0作为UI开发的库。
三、各部分功能说明
  1. 基础框架:使用官方的例程库作为基础框架。https://github.com/espressif/esp-dev-kits
    官方例程库下载下来后,找到esp32-s3-lcd-ev-board下examples里的lvgl_demos项目作为基础项目,在这个基础上叠加自己的功能。
  2. 现在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)

     

  3. 在main文件夹下,删除原有的“ui_printer”和“ui_tuner”文件夹,这两个文件夹,项目中用不到。再创建game文件夹,在game下创建文件夹“huarongdao”,用来存放自己的游戏代码文件。最后还需要修改一下CMakeLists.txt文件。
  4. 修改代码。在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();
    • /* Release the lock */
    • bsp_display_unlock();
    • }

     

  5. 游戏入口函数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();
  • }
  • }
  • }

 

 
四、作品源码
DigiKey_contest_2024用esp32-s3-lcd-ev-board制作华容道拼图游戏.doc (3.53 MB, 下载次数: 5)
五、作品功能演示视频
播放器加载失败: 未检测到Flash Player,请到安装
huard

 
六、项目总结
很伤心,没能完成最初设定的目标!本来想在这个板子上实现机器学习的内容的,实在是能力有限,无法完成。LVGL作为UI实现的工具,功能非常强大,可是总觉着版本有些混乱,不同版本方法差异好大,学习成本有点太高了,但是作为一个连接单片机和人的桥梁还是非常好用的,通过单片机、传感器来感知万物,再用合适的UI展示出来,与人互动。非常喜欢ESP32-S3-LCD-EV-Board开发板,希望能借助这个项目留下这块板子!

最新回复

厉害,厉害,这样看来其他GUI框架也能移植这个游戏了。   详情 回复 发表于 2024-10-21 16:59
点赞 关注
 
 

回复
举报

102

帖子

2

TA的资源

一粒金砂(中级)

沙发
 

感谢大佬分享

个人签名

点个灯吧

 
 
 

回复

7488

帖子

2

TA的资源

版主

板凳
 

这游戏有点意思,从零开发的呀?

点评

不是,移植开源的项目!  详情 回复 发表于 2024-10-18 12:26
 
 
 

回复

95

帖子

1

TA的资源

一粒金砂(高级)

4
 
wangerxian 发表于 2024-10-17 16:29 这游戏有点意思,从零开发的呀?

不是,移植开源的项目!

点评

厉害,厉害,这样看来其他GUI框架也能移植这个游戏了。  详情 回复 发表于 2024-10-18 13:10
 
 
 

回复

7488

帖子

2

TA的资源

版主

5
 
aramy 发表于 2024-10-18 12:26 不是,移植开源的项目!

厉害,厉害,这样看来其他GUI框架也能移植这个游戏了。

 
 
 

回复

429

帖子

10

TA的资源

纯净的硅(初级)

6
 

华容道规则不麻烦,但算法麻烦,小时候我背过81步解法,现在说是最短48步?

 
 
 

回复

1482

帖子

1

TA的资源

五彩晶圆(初级)

7
 

,如果可以,那很多小游戏都可以移植进来玩。。。。。。。。。。。。。。。

 
 
 

回复

2657

帖子

6

TA的资源

五彩晶圆(初级)

8
 

牛的

个人签名

希望做一些大家觉得好用的东西!

 
 
 

回复

1

帖子

0

TA的资源

一粒金砂(初级)

9
 

厉害,厉害,这样看来其他GUI框架也能移植这个游戏了。

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条
报名最后一周!2025 英飞凌消费、计算与通讯创新大会-北京站
会议时间:3月18日(周二)09:30签到
参会奖励:电动螺丝刀套装、户外登山包、京东卡

查看 »

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表