[Arduino资料] 零知开源——STM32F4实现ILI9486显示屏UI界面系列教程(一):电子书阅读器功能

[复制链接]
 楼主| 发表于 2025-6-24 18:25 | 显示全部楼层 |阅读模式
本帖最后由 lingzhiLab 于 2025-6-24 18:32 编辑
本教程将详细介绍如何在零知增强板上使用3.5寸ILI9486显示屏实现电子书阅读器功能。我们将使用LVGL库构建用户界面,并实现翻页、进度显示等核心功能。

一、硬件连接1.1 硬件组件清单
组件名称
规格参数
备注
零知开发板
STM32F407VGT6
主控制器
ILI9486显示屏
3.5英寸TFT LCD (480×320)
电阻触摸屏
SD卡模块
SPI接口,支持FAT32
存储电子书文件
XPT2046触摸控制器
SPI接口
集成在显示屏模块上
W25Q128 Flash芯片
128M-bit (16MB)
存储字体和系统文件
USB数据线
直流供电
系统电源

1.2 连接方式零知增强板设计有专门的显示屏接口,3.5寸ILI9486显示屏可直接插入增强板,无需额外连线
connection.png

电子书阅读器界面效果图:
8488731e-4f6f-4954-9450-04b0ea40125a(1).jpg

二、软件UI组件实现2.1 核心数据结构
  1. static const char* ebook_content[] = {
  2.     "Embedded Systems Fundamentals\n\n"
  3.     "Embedded systems are specialized computing systems that perform dedicated functions.",
  4.     "Microcontroller Architecture\n\n"
  5.     "Microcontrollers (MCUs) contain a processor core, memory, and programmable I/O peripherals.",
  6.     "Real-Time Operating Systems\n\n"
  7.     "RTOS provides deterministic timing behavior for embedded applications.",
  8.     "Communication Protocols\n\n"
  9.     "Wired: UART, SPI, I2C, CAN\nWireless: BLE, Wi-Fi, LoRaWAN",
  10.     "Development Lifecycle\n\n"
  11.     "Stages: Requirements, Design, Implementation, Testing, Deployment"
  12. };

  13. static const uint8_t ebook_page_count = sizeof(ebook_content) / sizeof(ebook_content[0]);

  14. // 电子书翻页事件回调
  15. static void ebook_prev_event_cb(lv_obj_t* btn, lv_event_t event);
  16. static void ebook_next_event_cb(lv_obj_t* btn, lv_event_t event);

  17. typedef struct {
  18.     uint8_t current_page;
  19.     uint8_t total_pages;
  20.     bool is_english;
  21.     lv_point_t touch_start;  // 触摸起始点
  22.     lv_obj_t* page_label;
  23.     lv_obj_t* progress_label;
  24.     lv_obj_t* btn_translate;
  25. } EBookState;


2.2 触摸事件处理
  1. // 声明全局ebook_state变量
  2. static EBookState ebook_state;

  3. // 3. 完全兼容的触摸事件处理(替代手势事件)
  4. static void ebook_touch_event_cb(lv_obj_t* obj, lv_event_t event) {
  5.     static lv_point_t touch_start;
  6.     static uint32_t touch_time;
  7.    
  8.     switch(event) {
  9.         case LV_EVENT_PRESSED: {
  10.             lv_indev_t* indev = lv_indev_get_act();
  11.             if(indev) {
  12.                 lv_indev_get_point(indev, &touch_start);
  13.                 touch_time = lv_tick_get();
  14.             }
  15.             break;
  16.         }
  17.         case LV_EVENT_RELEASED: {
  18.             lv_indev_t* indev = lv_indev_get_act();
  19.             lv_point_t touch_end;
  20.             if(indev) {
  21.                 lv_indev_get_point(indev, &touch_end);
  22.                
  23.                 // 计算滑动距离和时间
  24.                 lv_coord_t dx = touch_end.x - touch_start.x;
  25.                 uint32_t duration = lv_tick_elaps(touch_time);
  26.                
  27.                 // 判断有效滑动 (水平移动>30像素且时间<300ms)
  28.                 if(abs(dx) > 30 && duration < 300) {
  29.                     if(dx > 0) {
  30.                         // 向右滑动:上一页
  31.                         if(ebook_state.current_page > 0) {
  32.                             ebook_prev_event_cb(NULL, LV_EVENT_SHORT_CLICKED);
  33.                         }
  34.                     } else {
  35.                         // 向左滑动:下一页
  36.                         if(ebook_state.current_page < ebook_state.total_pages - 1) {
  37.                             ebook_next_event_cb(NULL, LV_EVENT_SHORT_CLICKED);
  38.                         }
  39.                     }
  40.                 }
  41.             }
  42.             break;
  43.         }
  44.     }
  45. }

  46. // 更新电子书显示
  47. static void update_ebook_display() {
  48.     // 重置位置避免动画残留
  49.     lv_obj_set_x(ebook_state.page_label, 0);
  50.     lv_label_set_text(ebook_state.page_label, ebook_content[ebook_state.current_page]);
  51.    
  52.     char progress[16];
  53.     snprintf(progress, sizeof(progress), "%d/%d", ebook_state.current_page + 1, ebook_state.total_pages);
  54.     lv_label_set_text(ebook_state.progress_label, progress);
  55. }



2.3 初始化界面
  1. void show_app_book() {
  2.     lv_obj_t* win = create_app_win("EBook Reader");
  3.     lv_coord_t hres = lv_disp_get_hor_res(NULL);
  4.     lv_coord_t vres = lv_disp_get_ver_res(NULL);
  5.    
  6.     // 初始化电子书状态
  7.     ebook_state.current_page = 0;
  8.     ebook_state.total_pages = ebook_page_count;
  9.     ebook_state.is_english = true; // 默认英文
  10.     ebook_state.touch_start.x = 0;
  11.     ebook_state.touch_start.y = 0;
  12.    
  13.     // 创建内容容器(支持触摸检测)
  14.     lv_obj_t* content_cont = lv_cont_create(win, NULL);
  15.     lv_obj_set_size(content_cont, hres - 40, vres - 100);
  16.     lv_obj_align(content_cont, NULL, LV_ALIGN_IN_TOP_MID, 0, 20);
  17.     // lv_obj_set_gesture_parent(content_cont, true);
  18.     lv_obj_set_event_cb(content_cont, ebook_touch_event_cb);
  19.    
  20.     // 创建页面标签
  21.     ebook_state.page_label = lv_label_create(content_cont, NULL);
  22.     lv_obj_set_width(ebook_state.page_label, lv_obj_get_width(content_cont) - 20);
  23.     lv_label_set_long_mode(ebook_state.page_label, LV_LABEL_LONG_EXPAND);
  24.     lv_label_set_align(ebook_state.page_label, LV_LABEL_ALIGN_LEFT);
  25.     lv_obj_set_width(ebook_state.page_label, lv_obj_get_width(content_cont) - 40); // 增加一些边距
  26.     lv_label_set_text(ebook_state.page_label, "");
  27.     lv_obj_align(ebook_state.page_label, NULL, LV_ALIGN_CENTER, 0, 0);
  28.    
  29.     // 创建进度标签
  30.     ebook_state.progress_label = lv_label_create(win, NULL);
  31.     lv_obj_align(ebook_state.progress_label, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, -30);
  32.     lv_label_set_text(ebook_state.progress_label, "0/0");
  33.    
  34.     // 初始显示
  35.     update_ebook_display();
  36. }


2.4 翻页功能实现
  1. // 翻页动画函数 (兼容旧版LVGL)
  2. static void ebook_page_anim(lv_obj_t* label, lv_coord_t start, lv_coord_t end) {
  3.     lv_anim_t a;
  4.     lv_anim_init(&a);
  5.     // lv_anim_set_var(&a, label);
  6.     lv_anim_set_values(&a, start, end);
  7.     lv_anim_set_time(&a, 300, 0);  // 兼容旧版API:设置持续时间和延迟
  8.     lv_anim_set_exec_cb(&a, label, (lv_anim_exec_xcb_t)lv_obj_set_x);  // 兼容旧版API
  9.     lv_anim_create(&a);
  10. }

  11. // 上一页事件
  12. static void ebook_prev_event_cb(lv_obj_t* btn, lv_event_t event) {
  13.     if(event == LV_EVENT_SHORT_CLICKED) {
  14.         if(ebook_state.current_page > 0) {
  15.             // 先设置新内容再动画
  16.             ebook_state.current_page--;
  17.             update_ebook_display();
  18.             
  19.             // 从左侧滑入动画
  20.             lv_obj_set_x(ebook_state.page_label, -lv_obj_get_width(lv_obj_get_parent(ebook_state.page_label)));
  21.             ebook_page_anim(ebook_state.page_label,
  22.                            -lv_obj_get_width(lv_obj_get_parent(ebook_state.page_label)),
  23.                            0);
  24.         }
  25.     }
  26. }

  27. // 下一页事件
  28. static void ebook_next_event_cb(lv_obj_t* btn, lv_event_t event) {
  29.     if(event == LV_EVENT_SHORT_CLICKED) {
  30.         if(ebook_state.current_page < ebook_state.total_pages - 1) {
  31.             ebook_state.current_page++;
  32.             update_ebook_display();
  33.             
  34.             // 从右侧滑入动画
  35.             lv_obj_set_x(ebook_state.page_label, lv_obj_get_width(lv_obj_get_parent(ebook_state.page_label)));
  36.             ebook_page_anim(ebook_state.page_label,
  37.                            lv_obj_get_width(lv_obj_get_parent(ebook_state.page_label)),
  38.                            0);
  39.         }
  40.     }
  41. }

2.5 功能说明
支持左右滑动翻页,滑动距离大于30像素且时间小于300ms时触发
屏幕两侧的箭头按钮提供物理翻页功能
使用LVGL动画实现平滑的翻页效果
底部显示当前页码和总页数
使用英文字体内容显示(中文字体需要进一步优化取模)

三、零知IDE配置3.1 项目设置
打开零知IDE,创建新项目
选择正确的开发板型号(零知增强板)
添加以下库依赖:
  • LVGL
  • ILI9486驱动
  • XPT2046触摸驱动

3.2 LCD屏幕驱动和初始化
  1. /* 与LCD驱动关联 */
  2. void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
  3. {
  4.          
  5.         u16 height,width;
  6.         u16 i,j;
  7.         width=area->x2 - area->x1+1;                         //得到填充的宽度
  8.         height=area->y2 - area->y1+1;                        //高度
  9.         for(i=0;i<height;i++)
  10.         {
  11.                 LCD_SetCursor(area->x1,area->y1+i);           //设置光标位置
  12.                 LCD_WriteRAM_Prepare();     //开始写入GRAM
  13.                 for(j=0;j<width;j++)
  14.                 {
  15.                         LCD_TYPE->LCD_RAM=color_p->full;//写入数据
  16.                         color_p++;
  17.                 }
  18.         }
  19.          
  20.         lv_disp_flush_ready(disp);
  21. }

  22. /* 中断 ms */
  23. static void lv_tick_handler(HardwareTimer*)
  24. {
  25.         lv_tick_inc(LVGL_TICK_PERIOD);
  26. }

  27. void lvgl_setup()
  28. {
  29.         lv_init();
  30.          
  31.         lv_disp_buf_init(&disp_buf, buf, NULL, LV_HOR_RES_MAX * 10);
  32.          
  33.         
  34.         lv_disp_drv_t disp_drv;
  35.         lv_disp_drv_init(&disp_drv);
  36.         disp_drv.hor_res = LV_HOR_RES_MAX;
  37.         disp_drv.ver_res = LV_VER_RES_MAX;
  38.         disp_drv.flush_cb = my_disp_flush;
  39.         disp_drv.buffer = &disp_buf;
  40.         lv_disp_drv_register(&disp_drv);
  41.          
  42.          
  43.       
  44.         lv_indev_drv_t indev_drv;
  45.         lv_indev_drv_init(&indev_drv);
  46.         indev_drv.type = LV_INDEV_TYPE_POINTER;
  47.         indev_drv.read_cb = my_touchpad_read;
  48.         lv_indev_drv_register(&indev_drv);
  49.          
  50.         
  51.         MyTim = new HardwareTimer(TIM2);
  52.         MyTim->setMode(2, TIMER_OUTPUT_COMPARE);   
  53.         MyTim->setOverflow(1000/LVGL_TICK_PERIOD, HERTZ_FORMAT);
  54.         MyTim->attachInterrupt(lv_tick_handler);
  55.         MyTim->resume();
  56. }


四、演示效果4.1 功能演示
打开电子书应用,显示第一页内容
向右滑动:切换到上一页内容
向左滑动:切换到下一页内容
进度更新:底部页码随翻页自动更新

4.2 视频演示
https://www.bilibili.com/video/BV19DKgzSEir/?spm_id_from=333.1387.homepage.video_card.click&vd_source=a31e3d8d8ce008260eee442534c2f63d

4.3 性能指标
项目
数值
说明
翻页响应时间
< 100ms
从触摸到页面开始动画的时间
动画帧率
30 FPS
翻页动画流畅度
内存占用
42KB
包括LVGL和电子书数据
刷新率
30Hz
显示屏刷新频率

五、常见问题解决
5.1 触摸不灵敏
解决方案
  • 检查触摸屏校准数据
  • 增加触摸检测阈值
  1. bool my_touchpad_read(lv_indev_drv_t * indev, lv_indev_data_t * data)
  2. {
  3.         static lv_coord_t last_x = 0;
  4.         static lv_coord_t last_y = 0;
  5.          
  6.         
  7.         data->state = ts.touched() ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
  8.         if(data->state == LV_INDEV_STATE_PR){
  9.                 TS_Point p = ts.getPoint();
  10.                  
  11.                
  12.                 last_x = LV_HOR_RES-(p.y *LV_HOR_RES)/4095;      
  13.                 last_y = (p.x *LV_VER_RES)/4095;      
  14.                  
  15.                 Serial.print("touched:");
  16.                 Serial.print(last_x);Serial.print(",");Serial.println(last_y);
  17.         }
  18.       
  19.         data->point.x = last_x;
  20.         data->point.y = last_y;
  21.          
  22.         return false;
  23. }


5.2 翻页卡顿
优化建议
  • 减少页面内容长度
  • 使用LVGL的局部刷新功能
  • 优化动画参数
  1. lv_anim_set_time(&a, 300, 0);  // 兼容旧版API:设置持续时间和延迟

六、总结与扩展

6.1 实现总结
本教程实现了电子书阅读器的核心功能:中文内容显示、触摸翻页、翻页动画效果、阅读进度显示

6.2 扩展建议
  • 添加书签保存和跳转功能
  • 实现字体大小切换
  • 添加暗色主题保护视力
  • 从SD卡加载电子书文件

6.3 下一步在下一个系列教程中,我们将实现日历显示及切换
✔零知开源是一个真正属于国人自己的开源软硬件平台,在开发效率上超越了Arduino平台并且更加容易上手,大大降低了开发难度。✔零知开源在软件方面提供了完整的学习教程和丰富示例代码,让不懂程序的工程师也能非常轻而易举的搭建电路来创作产品,测试产品。快来动手试试吧!
✔访问零知开源平台,获取更多实战项目和教程资源吧!
www.lingzhilab.com


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

本版积分规则

22

主题

33

帖子

0

粉丝
快速回复 返回顶部 返回列表