经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C 语言 » 查看文章
STM32 + RT-Thread + LVGL
来源:cnblogs  作者:浇筑菜鸟  时间:2024/6/11 10:00:50  对本文有异议

一、基本信息

  • MCU:STM32F103ZET6
  • RT-Thread:5.0.2
  • LVGL:8.3.11
  • LCD:ST7735s
  • 编译环境:RTThread studio

二、LVGL 移植要求

  • 16、32或64位微控制器或处理器
  • 建议速度大于16 MHz
  • 闪存/ROM: > 64 kB(建议180 kB)
  • 内存:8 kB(建议24 kB)
  • 1个帧缓冲器:在MCU、外部RAM或显示控制器中
  • LVGL的图形缓冲:>“水平分辨率”像素(推荐1/10“屏幕尺寸”)
  • C99或更新的编译器
  • 基本的C(或C++)知识:指针、结构、回调

三、添加 LVGL 软件包

  1. 添加软件包

  2. LVGL 文件目录

  3. lv_rt_thread_port.c 文件

    由上图可知:文件已经帮我们完成了三个函数的调用,只需要在对函数进行实例即可,由于我没用到触摸屏,所以将 lv_port_indev_init() 的调用屏蔽了

四、lv_user_gui_init() 函数

此函数的主要作用是 LVGL 启动的初始化界面,相当于开机界面,主要是消除初始化启动功能时导致屏幕出现长时间的白屏的现象,程序如下

点击查看代码
  1. #include <lvgl.h>
  2. void lv_user_gui_init(void)
  3. {
  4. /* 获取默认显示器的活动屏幕 */
  5. lv_obj_t *scr = lv_scr_act();
  6. lv_obj_clean(scr); /* 清屏 */
  7. /* 创建界面启动界面 */
  8. lv_obj_t *page = lv_obj_create(scr);
  9. lv_obj_set_size(page, LV_HOR_RES, LV_VER_RES);
  10. lv_obj_set_style_bg_color(page, lv_color_black(), LV_PART_MAIN); /* 设置背景颜色 */
  11. lv_obj_set_style_radius(page, 0, LV_PART_MAIN | LV_STATE_DEFAULT); /* 设置导角为0 */
  12. lv_obj_set_style_border_width(page, 0, LV_PART_MAIN | LV_STATE_DEFAULT); /* 设置边框为0 */
  13. /* 添加标签 */
  14. lv_obj_t *label = lv_label_create(page);
  15. lv_label_set_text(label, "Loading");
  16. lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
  17. }

五、lv_port_disp_init() 函数

此函数主要的作用是,初始化显示屏,并将显示屏的图像刷新函数与 flush_cb 函数进行绑定,程序如下

点击查看代码
  1. #include <lvgl.h>
  2. #include <rtthread.h>
  3. #include <board.h>
  4. //#define DRV_DEBUG
  5. #define LOG_TAG "LVGL.port.disp"
  6. #include <drv_log.h>
  7. static rt_device_t lcd_device = RT_NULL;
  8. static struct rt_device_graphic_info lcd_info;
  9. static lv_disp_drv_t disp_drv; /* 显示驱动程序的描述符 */
  10. /* 用于存储缓冲区的静态或全局变量 */
  11. static lv_disp_draw_buf_t disp_buf;
  12. void lv_port_disp_init(void)
  13. {
  14. rt_err_t result;
  15. void *lv_disp_buf1 = RT_NULL;
  16. void *lv_disp_buf2 = RT_NULL;
  17. /* 查找 LCD 设备 */
  18. lcd_device = rt_device_find("lcd");
  19. if (lcd_device == 0)
  20. {
  21. LOG_E("lcd_device error!");
  22. return;
  23. }
  24. result = rt_device_open(lcd_device, RT_DEVICE_FLAG_RDWR);
  25. if(result != RT_EOK)
  26. {
  27. LOG_E("open lcd device failed");
  28. return;
  29. }
  30. /* get framebuffer address */
  31. result = rt_device_control(lcd_device, RTGRAPHIC_CTRL_GET_INFO, &lcd_info);
  32. if (result != RT_EOK)
  33. {
  34. LOG_E("error!");
  35. /* get device information failed */
  36. return;
  37. }
  38. RT_ASSERT (lcd_info.bits_per_pixel == 8 || lcd_info.bits_per_pixel == 16 ||
  39. lcd_info.bits_per_pixel == 24 || lcd_info.bits_per_pixel == 32);
  40. lv_disp_buf1 = rt_malloc(lcd_info.smem_len * sizeof(lv_color_t));
  41. rt_memset(lv_disp_buf1, 0, lcd_info.smem_len * sizeof(lv_color_t));
  42. RT_ASSERT(lv_disp_buf1 != RT_NULL);
  43. lv_disp_buf2 = rt_malloc(lcd_info.smem_len * sizeof(lv_color_t));
  44. rt_memset(lv_disp_buf2, 0, lcd_info.smem_len * sizeof(lv_color_t));
  45. RT_ASSERT(lv_disp_buf2 != RT_NULL);
  46. /* 使用缓冲区初始化 disp_buf */
  47. lv_disp_draw_buf_init(&disp_buf, lv_disp_buf1, lv_disp_buf2, lcd_info.smem_len);
  48. lv_disp_drv_init(&disp_drv);
  49. /* 设置显示器的分辨率 */
  50. disp_drv.hor_res = lcd_info.width;
  51. disp_drv.ver_res = lcd_info.height;
  52. /* 设置显示缓冲区 */
  53. disp_drv.draw_buf = &disp_buf;
  54. /* 用于将缓冲区的内容复制到显示器 */
  55. disp_drv.flush_cb = lcd_device->user_data;
  56. /* 注册驱动程序 */
  57. lv_disp_drv_register(&disp_drv);
  58. }

六、ST7735s 驱动程序

这里不要局限于 ST7735s 这个显示屏,主要是介绍 LCD 与 LVGL 对接的 bsp 的编写过程。程序中的其他函数主要都是初始化 lcd 的工作,主要关注 lcd_fb_flush 函数,此函数会在 LVGL 中界面更新的时候调用,从而刷新屏幕的显示。

点击查看代码
  1. /**
  2. * @brief LCD 驱动的操作函数
  3. * @param device LCD 设备结构体
  4. * @param cmd 操作命令
  5. * @param args 传入的参数
  6. * @retval None
  7. */
  8. static rt_err_t drv_lcd_control(struct rt_device *device, int cmd, void *args)
  9. {
  10. LOG_D("drv_lcd_control cmd is: %d\n", cmd);
  11. switch (cmd)
  12. {
  13. case RTGRAPHIC_CTRL_RECT_UPDATE:
  14. break;
  15. case RTGRAPHIC_CTRL_POWERON:
  16. {
  17. /* LCD 退出睡眠模式 */
  18. lcd_display_on();
  19. lcd_exit_sleep();
  20. }
  21. break;
  22. case RTGRAPHIC_CTRL_POWEROFF:
  23. {
  24. /* LCD 进入睡眠模式 */
  25. lcd_display_off();
  26. lcd_enter_sleep();
  27. }
  28. break;
  29. case RTGRAPHIC_CTRL_GET_INFO:
  30. {
  31. /* 获取 LCD 参数 */
  32. memcpy(args, &lcd_info, sizeof(lcd_info));
  33. }
  34. break;
  35. default:
  36. return -RT_EINVAL;
  37. }
  38. return RT_EOK;
  39. }
  40. static void lcd_fb_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
  41. {
  42. rt_uint32_t px_size = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1);
  43. /* 设置屏幕刷新区域 */
  44. lcd_draw_area_set(area->x1, area->y1, area->x2, area->y2);
  45. /* 这里直接通过 SPI 发送数据,所以需要单独将数据引脚拉高 */
  46. rt_pin_write(LCD_DC_PIN, PIN_HIGH);
  47. /* SPI 发送时是 uint_8, 而像素是 uint_16 */
  48. rt_spi_send(spi_dev_lcd, color_p, px_size * 2);
  49. lv_disp_flush_ready(disp_drv);
  50. }
  51. /* 驱动函数实现的结构体 */
  52. #ifdef RT_USING_DEVICE_OPS
  53. const static struct rt_device_ops lcd_ops =
  54. {
  55. drv_lcd_init,
  56. RT_NULL,
  57. RT_NULL,
  58. RT_NULL,
  59. RT_NULL,
  60. drv_lcd_control
  61. };
  62. #endif
  63. /**
  64. * @brief LCD 设备注册
  65. *
  66. * @param None
  67. * @retval int 注册结果
  68. */
  69. int drv_lcd_hw_init(void)
  70. {
  71. rt_err_t result = RT_EOK;
  72. rt_uint32_t lcd_buff_size = LCD_HEIGHT * LCD_WIDTH * 2;
  73. device.user_data = lcd_fb_flush;
  74. /* 设置 LCD 设备信息 */
  75. lcd_info.height = LCD_HEIGHT;
  76. lcd_info.width = LCD_WIDTH;
  77. lcd_info.bits_per_pixel = LCD_BITS_PER_PIXEL;
  78. lcd_info.pixel_format = RTGRAPHIC_PIXEL_FORMAT_RGB565; // 图像的格式(RGB:565)
  79. /* LCD 显示缓冲区,大小为显示一帧图像所需空间 */
  80. lcd_info.smem_len = lcd_buff_size;
  81. #ifdef RT_USING_DEVICE_OPS
  82. device.ops = &lcd_ops;
  83. #else
  84. device.init = drv_lcd_init;
  85. device.control = drv_lcd_control;
  86. #endif
  87. /* 注册 LCD 设备 */
  88. result = rt_device_register(&device, "lcd", RT_DEVICE_FLAG_RDWR);
  89. return result;
  90. }
  91. INIT_DEVICE_EXPORT(drv_lcd_hw_init);

七、总结

从上面的过程可以看出,移植 LVGL 的过程很简单,最主要的是 lcd_fb_flush 函数的实现。需要注意的便是 lv_disp_flush_ready(disp_drv) 这个函数一定要添加,后面的界面可能不刷新,或者刷新不正常等现象。最后还需要添加一个头文件,如下所示

点击查看代码
  1. #ifndef LV_CONF_H
  2. #define LV_CONF_H
  3. #define LV_COLOR_DEPTH 16
  4. #endif /*LV_CONF_H*/

原文链接:https://www.cnblogs.com/jzcn/p/18241447

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号