移植LVGL到像素屏,从此玩转像素屏0门槛

硬件方面

先上渲染图

Untitled2.JPG

实物图

63b1c6cc5698bf18db31a922b621630.jpg

配置

  • 主控:esp32 micro32 plus
  • 主频:240Mhz
  • Flash:8M
  • PSRAM:2M

软件方面

众所周知,LVGL是一个十分优秀的图形框架,小到几百kb的单片机,大到Linux都可以运行。既然它这么优秀,各种组件又十分的全面,没道理不用。

跟着官方例程适配esp32

显示驱动

由于我的像素屏设计的是32*16尺寸的,使用的是512个WS2812B灯珠,所以LVGL官方适配的屏幕驱动是没法使用的,所以首先需要自己实现WS2812B的驱动,这里采用的是FastLED。然后直接去适配LVGL的绘制方法就可以了。
官方提供的lv_port_disp.cpp中有disp_flush函数,这个函数就是用来填充屏幕,只需要将它的每个像素绘制到屏幕中就可以了。

static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) 
{  
    if(disp_flush_enabled) {  
        /*将所有像素逐一放到屏幕上的最简单的情况(但也是最慢的情况)*/  
        int32_t x;  
        int32_t y;  
        for(y = area->y1; y <= area->y2; y++) {  
            for(x = area->x1; x <= area->x2; x++) {  
                /*Put a pixel to the display. For example:*/  
                /*put_px(x, y, *color_p)*/  
                // 设置像素点   
                pixels[matrixIndex[y * SCREEN_WIDTH + x]]=CRGB(color_p->ch.red,color_p->ch.green,color_p->ch.blue);  
                color_p++;  
            }  
        }  
        // 刷新  
        FastLED.show();  
    }  
    /*IMPORTANT!!!  
    *Inform the graphics library that you are ready with the flushing 最后必须得调用,通知 lvgl 库你已经 flushing 拷贝完成了*/  
    lv_disp_flush_ready(disp_drv);  
}

用disp_drv.hor_res和disp_drv.ver_res设置屏幕的宽高

/*Set the resolution of the display*/  
disp_drv.hor_res = MY_DISP_HOR_RES;  
disp_drv.ver_res = MY_DISP_VER_RES;

设置好这些,就可以显示了。

输入驱动

LVGL的输入控制方式有很多中可以选择,具体可以根据硬件灵活使用,由于我的硬件直设计了两个按钮,想要用两个按钮实现上下左右确认返回等控制是十分困难的,所以传统的按键映射控制是不行了,这里我采用的是模仿编码器控制,编码器只有上滚动、下滚动和确认;我将每个button按钮分成click和longClick两个事件,故两个按钮就可以产生四个事件,这样模仿编码器的三个事件足够了。

  • btn1 OnClick => left事件
  • btn2 OnClick => right事件
  • btn1 LongClick => enter事件
  • btn2 LongClick => esc事件

在LVGL例程中需要实现keypad_readkeypad_get_key函数;这里使用OneButton库来扫描按钮事件
由于OneButton只有按键事件回调,没有按键扫描函数,所以修改了一下OneButton的代码,使其可以返回按键状态。

/*Get the currently being pressed key. 0 if no key is pressed 获取当前按下的键。如果未按键,则为0*/  
static uint32_t keypad_get_key(void)  
{  
    /*Your code comes here*/  
    // OneButton 中修改了tick()函数,使其可以返回按键状态,用来获取扫描按键状态
    stateType_t type1 = buttons[0]->tick();  
    stateType_t type2 = buttons[1]->tick();  

    if (type1!=BTN_TYPE_NONE){  
            switch (type1) {  
                case BTN_TYPE_CLICK:  
                    return 1;  
                case BTN_TYPE_LONG_PRESS_START:  
                    return 3;  
            }  
        } else if (type2!=BTN_TYPE_NONE){  
            switch (type2) {  
                case BTN_TYPE_CLICK:  
                    return 2;  
                case BTN_TYPE_LONG_PRESS_START:  
                    return 4;  
        }  
    }  
    return 0;  
}

/*Will be called by the library to read the keypad*/  
static void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)  
{  
    static uint32_t last_key = 0;  

    /*Get the current x and y coordinates*/  
    // mouse_get_xy(&data->point.x, &data->point.y);  

    /*Get whether the a key is pressed and save the pressed key 获取是否按下了a键并保存按下的键 */  
    uint32_t act_key = keypad_get_key();  
    if(act_key != 0) {  
        data->state = LV_INDEV_STATE_PR;  

        /*Translate the keys to LVGL control characters according to your key definitions 根据您的密钥定义将密钥转换为LVGL控制字符*/  
        switch(act_key) {  
            case 1:  
                // 单击key1  
                act_key = LV_KEY_LEFT;  
                // Serial.println("LV_KEY_LEFT");  
                break;  
            case 2:  
                // 单击key2  
                act_key = LV_KEY_RIGHT;  
                // Serial.println("LV_KEY_RIGHT");  
                break;  
            case 3:  
                // 长按key1  
                act_key = LV_KEY_ENTER;  
                // Serial.println("LV_KEY_ENTER");  
                break;  
            case 4:  
                // 长按key2  
                act_key = LV_KEY_ESC;  
                // Serial.println("LV_KEY_ESC");  
                break;  
        }  
        last_key = act_key;  
    } else {  
        data->state = LV_INDEV_STATE_REL;  
    }  

    data->key = last_key;  
}

输入输出适配完成,就可以愉快的玩耍LVGL了,但是LVGL自带的字体是16px和8px的,并且还有亚像素渲染,导致字体显示虚化,有重叠。理论上16px和8px的字符都是可以显示的,但是由于有亚像素渲染,就会导致字体有重影,毕竟咱像素屏只有32*16的大小,最终的显示效果就是黏在一起辨认不清,而这还只是显示英文字符,汉字就更困难了,通用字库的汉字最小像素大小就是16px,即如果想要显示完整的汉字细节,咱整个屏幕一次只能放下两个汉字,体验感太差了。经过不懈努力下,我找到了一款8px的字体,十分的优秀,能够在32*16像素的屏幕能够足足显示8个汉字。并且模仿8段数码管,自定义了一套3*5大小的数字。

记录一下自定义字体的坑,LVGL自定义字体lv_font_fmt_txt_glyph_dsc_t类型的字段说明

uint32_t bitmap_index : 20; /**< 位图的起始索引。一个字体最多可以是1MB.*/  
uint32_t adv_w : 12; /**<在此宽度之后绘制下一个图示符。8.4格式(存储real_value*16).*/  
uint8_t box_w; /**< 图示符的边界框的宽度*/  
uint8_t box_h; /**< 图示符的边界框的高度*/  
int8_t ofs_x; /**< 边界框的x偏移*/  
int8_t ofs_y; /**< 边界框的y偏移。从线路顶部开始测量*/

划重点,adv_w属性为字符宽度,它的大小是16的倍数,即不管你的字符高多少,如果你的字符宽1,那么adv_w=16 宽2 adv_w=32;如果将adv_w理解成字符编码的宽度,那么3*5大小的字符adv_w=15,这样是不对的,它的adv_w应该是3*16=48.

应用程序框架设计

为了更愉快的玩转像素屏,方便以后的扩展功能,设计了一套App程序框架,该框架有App的启动、销毁等生命周期,任务栈功能,可以实现页面的切换,销毁等。

另外由于LVGL不是线程安全的,所以在多任务更新界面时,LVGL会有冲突,这里我参照Android的消息机制写了一个轻量级的多任务消息通知框架,这部分比较复杂,放在下篇文章单独写一下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/250506.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【第2期】Springboot如何快速集成SpringSecurity

简单介绍 本专栏主要结合实战讲解&#xff0c;不过多介绍细节的概念&#xff0c;概念可以通过搜索引擎查找&#xff0c;一搜一大把&#xff0c;切入正题。 本专栏的实战项目是基于SpringbootSpringSecurityRSAJWTVUE的全栈开发项目&#xff0c;每个环节都会专门讲&#xff0c;…

音频DAC,ADC,CODEC的选型分析,高性能立体声

想要让模拟信号和数字信号顺利“交往”&#xff0c;就需要一座像“鹊桥”一样的中介&#xff0c;将两种不同的语言转变成统一的语言&#xff0c;消除无语言障碍。这座鹊桥就是转换器芯片&#xff0c;也就是ADC芯片。ADC芯片的全称是Analog-to-Digital Converter, 即模拟数字转换…

MATLAB 计算两片点云间的最小距离(2种方法) (39)

MATLAB 计算两片点云间的最小距离 (39) 一、算法介绍二、算法实现1.常规计算方法2.基于KD树的快速计算一、算法介绍 假设我们现在有两片点云 1 和 2 ,需要计算二者之间的最小距离,这里提供两种计算方法,分别是常规计算和基于KD树近邻搜索的快速计算方法,使用的测试数据如…

遥感图像分割系统:融合空间金字塔池化(FocalModulation)改进YOLOv8

1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 研究背景与意义 遥感图像分割是遥感技术领域中的一个重要研究方向&#xff0c;它的目标是将遥感图像中的不同地物或地物类别进行有效的分割和识别。随着遥感技术的不断发展和遥感…

OceanBase数据库部署

文章目录 OceanBase基础概念集群、Zone和OB ServerRootService总控服务&#xff08;RS&#xff09;多租户机制&#xff1a;资源隔离&#xff0c;数据隔离每个租户拥有若干资源池&#xff08;Resource Pool&#xff09; 部署形式部署流程OceanBase客户端工具 学习体验部署实现 O…

挑战52天学小猪佩奇笔记--day24

52天学完小猪佩奇--day24 ​【本文说明】 本文内容来源于对B站UP 脑洞部长 的系列视频 挑战52天背完小猪佩奇----day24 的视频内容总结&#xff0c;方便复习。强烈建议大家去关注一波UP&#xff0c;配合UP视频学习。 注&#xff1a;这集开始变成一段一段的猜台词&#xff0c;加…

区块链的可扩展性研究【06】Plasma

1.Plasma&#xff1a;Plasma 是一种基于以太坊区块链的 Layer2 扩容方案&#xff0c;它通过建立一个分层结构的区块链网络&#xff0c;将大量的交易放到子链上进行处理&#xff0c;从而提高了以太坊的吞吐量。Plasma 还可以通过智能合约实现跨链交易&#xff0c;使得不同的区块…

Element的安装以及基本使用

Element是基于Vue的网站组件库&#xff0c;用于快捷构建网页 像上面这样的样式 官网地址 Element - 网站快速成型工具 安装 npm i element-ui -S 装包命令 npm install babel-plugin-component -D 安装好之后会在package.json里面显示版本 在node_modules中会自动初始化一个 …

云原生之深入解析OOM和CPU节流

一、前言 使用 Kubernetes 时&#xff0c;内存不足 (OOM) 错误和 CPU 节流是云应用程序中资源处理的主要难题&#xff0c;这是为什么呢&#xff1f;云应用程序中的 CPU 和内存要求变得越来越重要&#xff0c;因为它们与云成本直接相关。通过 limits 和 requests &#xff0c;可…

Java数据结构篇——单链表的基本操作

1. 前言 在上一篇《Java数据结构篇——实现顺序表的增删查改》&#xff0c;我们已经熟悉了 ArrayList 的使用并且进行了简单的模拟实现。ArrayList底层使用数组来存储元素&#xff0c;由于其底层是一段连续的空间&#xff0c;当ArrayList 任意位置插入或者删除元素时&#xff…

使用下载代替物理串口输出-STM32 Debug (printf) Viewer

使用下载代替物理串口输出-STM32 Debug 硬件要求配置方法代码要求打印输出结果 硬件要求 STM32的PB9、PB10引脚的串口1通常用作其他功能使用后&#xff0c;无法通过printf()函数打印输出想要调试输出查看变量或调试信息。现已使用另外一种方法实现printf()函数打印输出。 ST…

AutoGen多代理对话项目示例和工作流程分析

在这篇文章中&#xff0c;我将介绍AutoGen的多个代理的运行。这些代理将能够相互对话&#xff0c;协作评估股票价格&#xff0c;并使用AmCharts生成图表。 我们创建对话的目的是要求代理分析特定公司的股票价格&#xff0c;并制作股票价格图表。 为了实现这一目标&#xff0c;…

oracle DG 三种应用机制

首先理解不管是哪种机制&#xff0c;oracle都不是从主库直接传归档文件到备库&#xff0c;而是通过网络将主库的redo数据传输到备库&#xff1a; 1、普通DG是主库发生日志切换&#xff0c;备库把接收到的redo数据在备库通过归档进程生成为归档文件进行应用 2、ADG则是备库把接收…

Windows mysql5.7 执行查询/开启/测试binlog---简易记录

前言&#xff1a;基于虚拟机mysql版本为5.7&#xff0c;增量备份测试那就要用到binlog… 简述&#xff1a;二进制日志&#xff08;binnary log&#xff09;以事件形式记录了对MySQL数据库执行更改的所有操作。 binlog是记录所有数据库表结构变更&#xff08;例如CREATE、ALTER…

轻松搭建FPGA开发环境:第三课——Vivado 库编译与设置说明

工欲善其事必先利其器&#xff0c;很多人想从事FPGA的开发&#xff0c;但是不知道如何下手。既要装这个软件&#xff0c;又要装那个软件&#xff0c;还要编译仿真库&#xff0c;网上的教程一大堆&#xff0c;不知道到底应该听谁的。所以很多人还没开始就被繁琐的开发环境搭建吓…

在非联网、无网络环境下,fpm的安装和生成RPM包的使用案例

文章目录 前言1、安装fpm1.1、安装Ruby环境1.2、gem 安装 fpm 2、fpm使用2.1、fpm常用参数2.2、fpm使用案例2.2.1、fpmFirstDemo文件夹2.2.3、编写脚本文件2.2.4、生成RPM包2.2.5、RPM安装与卸载测试 前言 由于fpm采用Ruby语言开发&#xff0c;因此在使用之前需要先在您的虚拟…

Java只有值传递,没有引用传递!

结论&#xff1a;Java中的参数传递&#xff0c;只有值传递&#xff0c;没有引用传递&#xff01; 以下均为错误理解&#xff1a; 值传递和引用传递&#xff0c;区别在于传递的内容。如果是个值&#xff0c;就是值传递&#xff1b;如果是个引用&#xff0c;就是引用传递Java是引…

力扣日记12.13-【二叉树篇】从中序与后序遍历序列构造二叉树

力扣日记&#xff1a;【二叉树篇】从中序与后序遍历序列构造二叉树 日期&#xff1a;2023.12.13 参考&#xff1a;代码随想录、力扣 106. 从中序与后序遍历序列构造二叉树 题目描述 难度&#xff1a;中等 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二…

Prometheus

Prometheus [系统性能优化实践]JVM进阶实战之监控工具(Prometheus) https://www.cnblogs.com/johnnyzen/p/17388354.html ubuntu 22.04 配置 Prometheus 和 Grafana 服务器监控 https://blog.csdn.net/nvd11/article/details/128030197 Prometheus 是一个开源的监控系统&…

深入理解网络 I/O:单 Group 混杂模式|多 Group 主从模式

&#x1f52d; 嗨&#xff0c;您好 &#x1f44b; 我是 vnjohn&#xff0c;在互联网企业担任 Java 开发&#xff0c;CSDN 优质创作者 &#x1f4d6; 推荐专栏&#xff1a;Spring、MySQL、Nacos、Java&#xff0c;后续其他专栏会持续优化更新迭代 &#x1f332;文章所在专栏&…