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

news2024/9/23 5:15:48

硬件方面

先上渲染图

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的消息机制写了一个轻量级的多任务消息通知框架,这部分比较复杂,放在下篇文章单独写一下。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Qt QML 入门】TextEdit

TextEdit可以显示多行可编辑的格式化文。默认是无边框的&#xff0c;可以和父控件完美融合。 import QtQuick import QtQuick.Window import QtQuick.ControlsWindow {id: winwidth: 800height: 600visible: trueTextEdit {id: textEditanchors.centerIn: parenttext: "He…

【ArkTS】生命周期

页面生命周期 通常Entry修饰的组件称为页面&#xff0c;其拥有页面生命周期 onPageShow&#xff1a;页面每次显示时触发。onPageHide&#xff1a;页面每次隐藏时触发&#xff08;通常是路由跳转到其他页面了&#xff09;。onBackPress&#xff1a;当用户点击返回按钮时时触发…

谷歌的开源供应链安全

本内容是对Go项目负责人Russ Cox 在 ACM SCORED 活动上演讲内容[1]的摘录与整理。 SCORED 是Software Supply Chain Offensive Research and Ecosystem Defenses的简称, SCORED 23[2]于2023年11月30日在丹麦哥本哈根及远程参会形式举行。 摘要 &#x1f4a1; 谷歌在开源软件供应…

​SQL (关系型) 数据库-fastapi集成

SQL (关系型) 数据库 - FastAPI FastAPI不需要你使用SQL(关系型)数据库。 但是您可以使用任何您想要的关系型数据库。 在这里&#xff0c;让我们看一个使用着SQLAlchemy的示例。 您可以很容易地将SQLAlchemy支持任何数据库&#xff0c;像&#xff1a; PostgreSQLMySQLSQLi…

Visual Studio 2022封装C代码为x64和x86平台动态库

1.引言 本文介绍如何使用Visual Studio 2022将C语言函数封装成x64和x86平台上使用的动态链接库(dll文件)并生成对应的静态链接库(lib文件)&#xff0c;以及如何在C程序中调用生成的dll。 程序下载&#xff1a; 2.示例C语言程序 假设需要开发一个动态链接库&#xff0c;实现复…

Linux(操作系统)面经——part 1(持续更新中......)

1、说一说常用的 Linux 命令 mkdir创建文件夹&#xff0c;touch创建文件&#xff0c;mv移动文件内容或改名 rm-r 文件名&#xff1a;删除文件 cp拷贝&#xff1a;cp 文件1 文件2&#xff0c;cp-r跨目录拷贝 cp-r 路径1 路径2 vi 插入 &#xff1a;wqb保存退出 :q!强制退出…

W25N01GV 芯片应用

项目中处于成本考虑&#xff0c;要把Nor Flash换成低成本的Nand Flash。 这里总结下芯片应用。 总体概述&#xff1a; 1&#xff09;W25N01&#xff08;NandFlash&#xff09;和W25Q&#xff08;Nor Flash&#xff09;的操作大不一样。 NandFlash擦除以块&#xff08;128KB&…

计算BMI指数-第11届蓝桥杯选拔赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第19讲。 计算BMI指数&…

【TB作品】基于单片机的机械通风控制系统,实时温度和二氧化碳浓度

硬件&#xff1a; &#xff08;1&#xff09;51系列单片机&#xff0c;拟采用STC89C52RC&#xff1b; &#xff08;2&#xff09;DS18B20温度传感器&#xff1b; &#xff08;3&#xff09;二氧化碳浓度传感器&#xff1a;https://item.taobao.com/item.htm?spma21n57.1.0.0.1…

DS八大排序之冒泡排序和快速排序

前言 前两期我们已经对"插入排序"&#xff08;直接插入排序和希尔排序&#xff09; 和 "选择排序"&#xff08;直接选择排序和堆排序&#xff09;进行了详细的介绍~&#xff01;这一期我们再来详细介绍一组排序 &#xff1a;"交换排序"即耳熟能…

lv12 uboot移植深化 9

u-boot-2013.01移植 【实验目的】 了解u-boot 的代码结构及移植的基本方法 【实验环境】 ubuntu 14.04发行版FS4412实验平台交叉编译工具arm-none-linux-gnueabi- 【注意事项】 实验步骤中以“$”开头的命令表示在 ubuntu 环境下执行 【实验步骤】 1 建立自己的平台 1.…

在线客服系统定价因素解析:影响价格的关键因素

跨境电子商务公司必不可少的工具就是在线客服系统。企业选择在线客服系统的时候免不了要对不同产品的功能性、价格、服务等因素进行考量。今天这篇文章&#xff0c;我们就来探讨一下在线客服系统的定价因素有哪些&#xff1f;探究市面上的在线客服系统价格各异的影响因素。为大…

libp2p 快速开始

文章目录 第一部分&#xff1a;libp2p 快速入门一、什么是libp2plibp2p 发展历程libp2p的特性p2p 网络和我们熟悉的 client/server 网络的区别&#xff1a; 二、Libp2p的实现目标三、Libp2p的用途四、运行 Libp2p 协议流程libp2p 分为三层libp2p 还有一个局域网节点发现协议 mD…

27系列DGUS智能屏发布:可实时播放高清模拟信号摄像头视频

针对高清晰度的模拟信号摄像头视频画面的显示需求&#xff0c;迪文特推出27系列DGUS智能屏。该系列智能屏可适配常见的AHD摄像头、CVBS摄像头&#xff0c;支持单路1080P高清显示、两路720P同屏显示&#xff08;同一类型摄像头&#xff09;。用户通过DGUS简单开发即可实现摄像头…

netty-daxin-3(rpc远程调用)

文章目录 nettyRpcObjectEncoder 与 ObjectDecoderjdk动态代理回顾Rpc调用过程简析服务端客户端 nettyRpc ObjectEncoder 与 ObjectDecoder ObjectEncoder继承自MessageToByteEncoder<Serializable>&#xff0c;它内部使用ByteBufOutputStream包装ByteBuf对象&#xff…

SpringBoot 自动装配原理---源码详解

目录 SpringBoot 自动装配原理源码流程详解&#xff1a;流程总结&#xff1a;条件匹配解释&#xff1a;其他解释&#xff1a; SpringBoot 自动装配原理 源码流程详解&#xff1a; 1、先看启动类&#xff0c;启动这个main方法&#xff0c;然后调用这个run方法。 2、把 启动类作…

一文了解Tomcat

文章目录 1、Tomcat介绍2、Tomcat使用配置2.1、Tomcat下载启动2.2、Tomcat启动乱码2.3、Tomcat端口号修改 3、Tomcat项目部署4、IDEA中使用Tomcat方式 1、Tomcat介绍 什么是Tomcat ​ Tomcat是Apache软件基金会一个核心项目&#xff0c;是一个开源免费的轻量级web服务器&#x…

Networkx实现小世界网络的分析

Networkx实现小世界网络的分析 小世界网络 小世界现象&#xff0c;也被称为六度分离原则&#xff0c;即如果你在地球上的任何地方随便选择任何两个人&#xff0c;你会发现一条至多由他们之间的6个熟人形成的路径。在网络科学语言中&#xff0c;六度也被称为小世界性质&#x…

C++相关闲碎记录(16)

1、正则表达式 &#xff08;1&#xff09;regex的匹配和查找接口 #include <regex> #include <iostream> using namespace std;void out (bool b) {cout << ( b ? "found" : "not found") << endl; }int main() {// find XML/H…

【C++干货铺】继承后的多态 | 抽象类

个人主页点击直达&#xff1a;小白不是程序媛 C系列专栏&#xff1a;C干货铺 代码仓库&#xff1a;Gitee 目录 多态的概念 多态的定义和实现 多态的定义条件 虚函数 虚函数的重写 特殊情况 协变&#xff08;基类和派生类的虚函数返回值不同&#xff09; 析构函数的重…