《ESP32-Arduino》LVGL之输入设备详解及实例(触摸屏,实体按键,编码器,多功能按键)

2023-10-31

前言: 好久没写博客了,一方面是平时着实没有时间,另一方面是知识还是欠缺,实在没啥技术拿得出手(其实更主要的还是懒!!!)最近玩的比较多的就是LVGL了,自己也是做了几个小项目(后续考虑开源),考虑到网上LVGL入门教程还是比较少,特此出来写篇博客。

对于LVGL就不过多介绍了,能点进来的应该都知道LVGL是什么吧,本篇博客不讲UI中的相关组件,而侧重于讲解对于LVGL中的输入设备,什么是输入设备呢?对于LVGL来说,输入设备有:

  • LV_INDEV_TYPE_POINTER:触摸板或鼠标
  • LV_INDEV_TYPE_KEYPAD: 键盘
  • LV_INDEV_TYPE_ENCODER:编码器
  • LV_INDEV_TYPE_BUTTON:外部虚拟按钮

而对于大多数项目来说,用触摸屏,实体按键,编码器的比较多(打死我也不说是其他的我都没用过),那么如何将这些设备与LVGL中的组件相关联就是本篇博客的主要目的。

前期准备

环境

  • VScode+Platformio+LVGL工程(相关教程见【传送门】)

注:本博客虽是基于Arduino写的,但掌握原理,在其他平台一样能使用

硬件

  • ESP32
  • 带触摸或不带触摸的TFT显示屏
  • 物理按键 / 编码器(如EC11)/ 多功能按键(如SLLB120200)

知识储备

  • 对应的LVGL文档页面

流程讲解

在摆代码之前,先过一下流程,因为所有的输入设备都是基于这一套流程走的,代码都大同小异
在这里插入图片描述
这里面my_indev_read是一个函数名,其作用就是不断读取输入设备的状态,当检测到输入设备的状态与之前的状态不同时就会更新状态并做出相应的响应,响应事件见【传送门

实例

所有的实例都是基于LVGL官方模板所写的,模板详见【传送门】,以下实例的使用方法就是将其建成一个.cpp文件,并新建一个同名.h文件,.h文件用于函数声明且和在其他文件中调用,.c文件用于函数定义(这些都是c语言的基础知识,实在不懂的自行百度吧),这里的输入设备初识化只需要在你工程初识化的地方调用lv_port_indev_init()函数即可

触摸屏

触摸屏反馈及初识化函数用的是第三方库【TFT_eSPI】中的相关内容

#include <lvgl.h>
#include <TFT_eSPI.h>

// 触摸屏状态更新函数
static void my_touchpad_read( lv_indev_drv_t * indev_driver, lv_indev_data_t * data )
{
    uint16_t touchX, touchY;

    bool touched = tft.getTouch( &touchX, &touchY, 600 );

    if( !touched )
    {
        data->state = LV_INDEV_STATE_REL;
    }
    else
    {
        data->state = LV_INDEV_STATE_PR;

        /*Set the coordinates*/
        data->point.x = touchX;
        data->point.y = touchY;

        //Serial.print( "Data x " );
        //Serial.println( touchX );
        //Serial.print( "Data y " );
        //Serial.println( touchY );
    }
}

// 触摸屏初识化函数
static void my_touchpad_init()
{
	// 以下两种方式的功能都是触摸屏的校准,TFT_eSPI用的是第一种,而LVGL官方用的是第二种,我也不知道那种方式更好
	 // Calibrate the touch screen and retrieve the scaling factors
	 touch_calibrate();
	
	/*
	 // Replace above line with the code sent to Serial Monitor
	 // once calibration is complete, e.g.:
	 uint16_t calData[5] = { 286, 3534, 283, 3600, 6 };
	 tft.setTouch(calData);
	*/
}

// 输入设备初识化函数
void lv_port_indev_init(void)
{
	// 初识化触摸屏
	my_touchpad_init();
	// 注册输入设备
	static lv_indev_drv_t indev_drv;
	lv_indev_drv_init( &indev_drv );
	indev_drv.type = LV_INDEV_TYPE_POINTER;
	indev_drv.read_cb = my_touchpad_read;
	lv_indev_drv_register( &indev_drv );
}

实体按键

实体按键可以使用第三方库【MD_UISwitch】作为按键的反馈(支持长按阶段性反馈,即长按的话隔一段时间就切换下一个组件,但这样就不能响应LV_EVENT_LONG_PRESSED中的长按响应事件了),也可以直接使用Arduino中digitalWrite()读取引脚电平或者其他单片机中读取引脚电平的方法(这种方式支持LV_EVENT_LONG_PRESSED中的长按响应事件,但不能长按阶段性反馈),看各位取舍吧,我的建议就是一般导航键都有3个按键,左右及确定键,左右键使用MD_UISwitch,中间确定键使用digitalWrite()

#include <lvgl.h>
#include "MD_UISwitch.h"

#define PinA 1	// 左键引脚
#define PinB 2  // 右键引脚
#define PinC 3  // 确定键引脚

MD_UISwitch_Digital Key_L(PinA, (uint8_t)LOW);
MD_UISwitch_Digital Key_R(PinB, (uint8_t)LOW);

// 按键扫描函数,用于区分哪个按键按下了
uint8_t Key_Scan()
{
	if (digitalRead(PinC) == LOW)
	{
		//Serial.Println("[Navigation] click");
		return 1;
	}
	MD_UISwitch::keyResult_t k_r = Key_R.read();
	MD_UISwitch::keyResult_t k_l = Key_L.read();

	if (k_l == MD_UISwitch::KEY_PRESS)
	{
		//Serial.Println("[Navigation] prev");
		return 2;
	}
	else if (k_r == MD_UISwitch::KEY_PRESS)
	{
		//Serial.Println("[Navigation] next");         
		return 3;			
	}
	
	return 0;// 没按下返回0
}

// 按键状态更新函数
static void my_key_read( lv_indev_drv_t * indev_driver, lv_indev_data_t * data )
{
	static uint32_t last_key = 0;
	uint8_t act_enc = Key_Scan();
	
	if(act_enc != 0) {
	    switch(act_enc) {
	        case 1:
	            act_enc = LV_KEY_ENTER;
	            data->state = LV_INDEV_STATE_PRESSED;	
	            break;
	        case 2:
	            act_enc = LV_KEY_RIGHT;
	            data->state = LV_INDEV_STATE_RELEASED;
	            data->enc_diff++;
	            break;
	        case 3:
	            act_enc = LV_KEY_LEFT;
	            data->state = LV_INDEV_STATE_RELEASED;
	            data->enc_diff--;
	            break;
	    }
	    last_key = (uint32_t)act_enc;
	}
	data->key = last_key;
}

// 按键初识化函数
static void my_key_init()
{
	Key_L.enableDoublePress(false);	// 是否允许双击
	Key_L.enableLongPress(false);	// 是否允许长按
	Key_R.enableDoublePress(false);	// 是否允许双击
	Key_R.enableLongPress(false);	// 是否允许长按
	pinMode(PinC, INPUT_PULLUP);
}

// 输入设备初识化函数
void lv_port_indev_init(void)
{
	// 初识化按键
	my_key_init();
	// 注册输入设备
	static lv_indev_drv_t indev_drv;
	lv_indev_drv_init( &indev_drv );
	// 说一下这里的类型为什么要设置为编码器类型,因为我试过很多遍LV_INDEV_TYPE_KEYPAD都没成功,
	// 我觉得是这里的KEYPAD可能指的是键盘鼠标的那种键盘,所以就没成功,当然也可能单纯是我菜
	indev_drv.type = LV_INDEV_TYPE_ENCODER;
	indev_drv.read_cb = my_key_read;
	lv_indev_drv_register( &indev_drv );
}

多功能按键(或叫波轮按键)

多功能按键并不属于编码器类型,虽然它们之间有些形状类似,但原理截然不同,多功能按键本质上就是按键
外形图
多功能按键一共有6个引脚,其中C为公共脚,一般接地或接VCC
在这里插入图片描述
当多功能按键中间键按下时,C脚和T脚接通,因此C+T脚可以组成一个按键开关
在这里插入图片描述
如图,图中黑色部分表示接通,如当波轮开关顺时针旋转11°时1,C脚接通,当波轮开关继续旋转到21.5°时1,2,C脚接通,因此1,2都可以分别和C脚组成一个开关,当转动角度不同时,1,2引脚被相继置为低电平或高电平(根据C引脚决定)

知道了多功能按键的原理,使用起来也非常简单,只需要把多功能按键看做按键使用,将1,4,T(或2,3,T)引脚分别接到单片机IO口上,C引脚接地就能组成上述实体按键一样的效果,代码就不展示了,稍微变通一下就行

编码器

编码器的类型有各式各样,但原理都相同(原理我也没怎么明白,就不丢人了),其都有ABC及S四个引脚(有些有多个S引脚),A,B引脚为左右旋的时候触发,C引脚为按下时触发,因此可以将S引脚接地,AB及C引脚接单片机IO口组成导航键,这里的编码器反馈用到了第三方库【MD_REncoder】,没办法,Arduino好就好在第三方库多,完全不用懂原理就能用(窃喜)

#include <lvgl.h>
#include "MD_REncoder.h"

#define PinA 1	// 左键引脚
#define PinB 2  // 右键引脚
#define PinC 3  // 确定键引脚

static MD_REncoder R = MD_REncoder(PinA, PinB);		//旋转编码器

// 编码器扫描函数,用于判断左右及按下状态
uint8_t Encoder_Scan()
{
	if (digitalRead(PinC) == LOW)
	{
		//Serial.Println("[Navigation] click");
		return 1;
	}
	uint8_t x = R.read();
	if (x)
	{
		// x == DIR_CW;
		if (x == DIR_CW ) {
			//Serial.println("[Navigation] next");         
			return 2;
		}
		else
		{
			//Serial.println("[Navigation] prev");
			return 3;
		}
	}
	return 0;// 没按下返回0
}

// 编码器状态更新函数
static void my_encoder_read( lv_indev_drv_t * indev_driver, lv_indev_data_t * data )
{
	static uint32_t last_key = 0;
	uint8_t act_enc = Encoder_Scan();
	
	if(act_enc != 0) {
	    switch(act_enc) {
	        case 1:
	            act_enc = LV_KEY_ENTER;
	            data->state = LV_INDEV_STATE_PRESSED;	
	            break;
	        case 2:
	            act_enc = LV_KEY_RIGHT;
	            data->state = LV_INDEV_STATE_RELEASED;
	            data->enc_diff++;
	            break;
	        case 3:
	            act_enc = LV_KEY_LEFT;
	            data->state = LV_INDEV_STATE_RELEASED;
	            data->enc_diff--;
	            break;
	    }
	    last_key = (uint32_t)act_enc;
	}
	data->key = last_key;
}

// 按键初识化函数
static void my_encoder_init()
{
	R.begin();
	pinMode(PinC, INPUT_PULLUP);
}

// 输入设备初识化函数
void lv_port_indev_init(void)
{
	// 初识化编码器
	my_encoder_init();
	// 注册输入设备
	static lv_indev_drv_t indev_drv;
	lv_indev_drv_init( &indev_drv );
	indev_drv.type = LV_INDEV_TYPE_ENCODER;
	indev_drv.read_cb = my_encoder_read;
	lv_indev_drv_register( &indev_drv );
}

将输入设备与组件绑定

输入设备初识化成功后如何将其与LVGL中的组件进行绑定呢?这是很多教程中没有说明的,也是我踩了很多坑之后才知道的。这里就需要知道一个lv_group_t的概念,group是LVGL中很重要的一部分,其作用就是将许多LVGL中的组件划分为不同的组,输入设备可以通过切换绑定的组从而控制不同组中的组件,这里介绍几个group中常用的函数,更多函数见【传送门

  • lv_group_t * lv_group_create(void)
    • 作用:创建一个lv_group_t实例,如 lv_group_t* group = lv_group_create()
  • void lv_group_del(lv_group_t* group)
    • 作用:删除一个lv_group_t实例
  • void lv_group_set_default(lv_group_t * group)
    • 作用:设置一个group实例为默认组
  • lv_group_t * lv_group_get_default(void)
    • 作用:获取默认组
  • void lv_group_add_obj(lv_group_t * group, lv_obj_t * obj)
    • 作用:group填加组件,只有在group中添加的组件才能受到控制
  • void lv_group_remove_obj(struct _lv_obj_t * obj)
    • 作用:group移除组件
  • void lv_group_remove_all_objs(lv_group_t * group)
    • 作用:group移除所有组件
- void lv_group_set_editing(lv_group_t * group, bool edit)
  - 作用:设置group为编辑模式或者导航模式,这里的编辑模式只对如下拉列表,按键矩阵等有二级控件时有用,一般来说这些在导航模式都需要先点击确定才能编辑,而在编辑模式下,无需确定即可编辑
- void lv_indev_set_group(lv_indev_t * indev, lv_group_t * group)
  - 作用:将输入设备与group相绑定,这里的输入设备指lv_indev_drv_register()返回的值,这个最重要了, 前面初识化,添加组件都弄了,要是最后没绑定,一切都白搭

这里给个例子吧

	lv_obj_t* btn1 = lv_btn_create(NULL);
	lv_obj_t* btn2 = lv_btn_create(NULL);
	
	// group可以在void lv_port_indev_init()函数中就创建,而在窗口中通过
	// lv_group_t* group = lv_group_get_default();
	// lv_group_add_obj(group, obj);
	// 的方式添加组件,更多的自己慢慢探索吧
	group = lv_group_create();
	lv_group_set_default(group);
	lv_group_remove_all_objs(group);
	lv_group_add_obj(group, btn1);
	lv_group_add_obj(group, btn2);
	// 这里的indev_encoder是lv_indev_drv_register( &indev_drv )的返回值
	// 案例中均没有使用到这个值
	lv_indev_set_group(indev_encoder, group);

最后

LVGL官方最近出了一个图形化工具,根据简单的拖拽即可导出UI代码,支持最新的LVGL8.2版本,不过目前支持的组件不多,而且是付费应用(有30天试用期),感兴趣的见【传送门】。
我还是建议自己敲代码,尽管这样会踩很多坑,但毕竟踩坑才能进步,才能成长。
最后,看反馈不定期更下一期!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

《ESP32-Arduino》LVGL之输入设备详解及实例(触摸屏,实体按键,编码器,多功能按键) 的相关文章

  • systick定时器

    systick定时器 文章目录 前言 一 前期疑惑 二 解答 1 关于systick是阻塞的吗 2 非阻塞 三 软件编写 总结 前言 这边记录systick相关知识点 一 前期疑惑 在学习systick志气啊 其实对于systick还是一脸
  • 批处理文件:从串口接收数据并将其写入txt文件

    我正在尝试通过 USB 串行端口从 Arduino 提取一些数据并将其写入 txt 文件 因此 我使用批处理文件 Windows 7 普通cmd 它发送例如 d 从 Arduino 请求所需的数据 收到 d 后 Arduino 开始向 PC
  • Arduino Python3 脚本

    我正在尝试使用 Python3 脚本来控制 Arduino Mega 这是一个简单的脚本 用于从键盘获取一行并通过 Arduino 回显它 我从一个可用的 Python 2 脚本开始http petrimaki wordpress com
  • Mac OS 10.9 不显示 Arduino 的 USB 调制解调器

    我正在尝试选择 dev tty usbmodem on my Arduino Lenardo设备 操作系统是Mac OSX 10 9 问题是它没有显示 我什至尝试安装FTDI http www ftdichip com Drivers VC
  • 如何使用arduino从sim900模块的RTC读取日期和时间数据?

    include SIM900 h include
  • 如何将 char 数组定义为常量?

    这里是 C C 菜鸟 我已经在头文件中定义了它 typedef unsigned char BitChar 9 8 data bytes chars and one width byte char extern BitChar BitFon
  • PySerial 从 Arduino 读取线路的延迟

    我正在使用带有基本 DigitalReadSerial 设置的 arduino uno 如下所述 http arduino cc en Tutorial DigitalReadSerial http arduino cc en Tutori
  • Arduino:使用串口和软件串口与蓝牙模块

    我的目的是使用 Arduino 通过 HC 05 蓝牙模块在 PC 和 Android 设备之间建立通信 我使用 PC 和 Arduino 串行监视器 之间的 USB 通信以及 SoftwareSerial 来连接到 HC 05 我的问题是
  • Android BLE 扫描与 UUID 过滤器列表混淆

    我正在 Android 移动设备 A 上执行 UUID 过滤扫描 并且有两个测试用例 一个人在另一台 Android 设备 移动设备 b 上使用名为 BLE Scanner 的应用程序来创建具有两个测试服务 每个服务都有自己唯一的 UUID
  • 静态 uint8_t 数组的输入过程和类型

    我目前正在尝试将 Arduino IDE 中的整数变量转换为静态 uint8 t 数组的值 我在用 include
  • C++:将成员函数作为普通函数指针传递的闭包

    我正在尝试调用外部库的成员函数 该函数接受函数指针作为参数 Timer every unsigned long period void callback void 但不幸的是我想传递的参数是一个成员函数 void MyClass the m
  • 终止导致设备或资源繁忙的进程:“/dev/ttyUSB0”?

    我使用以下 Python 代码连接到我的 Arduino 板 device glob glob dev ttyUSB 0 time sleep 1 arduino serial Serial device 115200 timeout 5
  • 带/不带类的回调函数指针 C++

    我被困 我正在尝试形成一个函数 它将吃掉无类函数指针和对象中的函数指针 这是我当前的代码 希望能解释更多 它应该在 Arduino 上运行 所以我不能使用大型库 首先 我在 Arduino 上使用这个库 SimpleTimer A time
  • 使用串口通过 Arduino 将多个值发送到 Raspberry

    我有一个关于 Arduino 和 Raspberry Pi 之间串行通信的问题 事实上 我想使用 Arduino 将 2 个变量发送到 Raspberry Pi 并以不同的方式使用它们 这是我的 Arduino 草图 int one 1 i
  • SIM900 GSM/GPRS 未获得正确的 AT+CREG?回答

    我使用的是带有 IComsat SIM900 GSM GPRS 扩展板的 Arduino UNO 使用以下教程 Arduino 实时 GPS 追踪器 http www samaria me uk 2011 12 arduino live g
  • 是否可以在 Arduino 上运行 ZeroMQ

    我想知道是否可以通过 ZeroMQ 与 Arduino 进行通信 我想使用 Arduino 来控制一些设备 并希望将大部分应用程序放在计算机上 为此 如果 Arduino 和控制器可以通过 Zeromq 以及 WiFi 或以太网扩展板进行通
  • 如何在 Arduino 中将 char 变量作为数字打印到串行端口?

    我正在使用 Arduino Uno 我正在从 EEPROM 读取一个字节值并将该值存储在类型变量中char 1 字节 我想将变量的值作为数字 而不是相应的 ASCII 代码 打印到串行监视器 例如考虑char val 5 我想查看串行监视器
  • ESP32:dsb1820 温度传感器给出恒定的负 127 读数

    我正在尝试使用连接到 esp32 微控制器的单个 dsb1820 温度传感器来获取温度读数 传感器连接到 esp32 的 GPIO 4 我打算将温度读数发送到云端 我面临的问题是温度读数总是给出值 127 我在网上某处读到 当 dsb182
  • 仅从日期计算夏令时

    我正在使用 Arduino 和实时时钟芯片 该芯片补偿了闰年等 因此它始终具有正确的日期 但我认为由于区域复杂性 它不处理夏令时 时钟可以给出日 月 年 从 1 开始 以及星期几 星期日 0 到星期六 6 因为我需要与用户输入的日期和时间进
  • Arduino 错误:未命名类型?

    我已经编写了一个库 但遇到错误未命名类型的问题 我已经尝试了一切 搜索了几个小时 但没有运气 库放置在arduino sketch文件夹的 libraries 文件夹中 请帮忙 我使用的是 OSX 但 Windows 上也出现同样的问题 这

随机推荐

  • 资产管理系统 CMDB 详解

    两年前笔者在一个中小型互联网公司做运维 当时我们经理在机房 花了半天找一台服务器 但是服务器搞错了 悲剧了 当时我们的做法是用了一个 Excel 很多时候更新不及时 重启一台机器 拔一根网线都提心吊胆 那个时候如果有一个资产管理系统的话就可
  • ROS学习2 - 集成开发环境

    集成开发环境 一 终端 二 vscode安装 1 安装 vscode 2种方式 2 卸载 vscode 3 将vscode添加到右侧的收藏夹 4 安装插件 三 vscode使用 1 创建工作空间 2 启动vscode 3 配置编译 5 创建
  • cadence 介绍

    转载自 SI list 中国 http mp weixin qq com s qsdfzQwIVjvwHXuCdvrPXA 本篇对2017年初版Cadence的全套所有EDA工具的技术特性特点做一深入的分析 并与EDA其它主流厂商的对应工具
  • 用数据库修改服务器的时间格式,如何查询数据库服务器的时间格式

    如何查询数据库服务器的时间格式 内容精选 换一换 CDM支持文件类数据到表的迁移 本章节以OBS gt MySQL为例 介绍如何通过CDM将文件类数据迁移到表中 流程如下 创建CDM集群并绑定EIP创建MySQL连接创建OBS连接创建迁移作
  • JDBC 连接MySQL 数据库并查询数据

    JDBC 连接MySQL 数据库并查询数据 一 Mysql安装 二 JDBC 连接MySQL 一 Eclipse新建项目 二 导入Mysql的包 三 建立数据库并查询数据 一 新建mysql数据库 二 JDBC 连接MySQL 数据库并查询
  • STC15F2K60S2内部EEPROM的读写操作

    一 STC15F2K60S2单片机内部EEPROM的介绍 STC15系列单片机内部集成了大容量的EEPROM 其与程序空间是分开的 利用ISP IAP技术可将内部Date Flash当EEPROM 擦写次数在10万次以上 EEPROM可分为
  • 修复PHP扫描漏洞

    1 TLS 1 0 enabled 报告描述 修复方案 参考网站 https blog csdn net a1368783069 article details 85064682 如果使用chrome浏览器依次打开 右键 gt 审查 ins
  • python paramiko模块sftp异常:paramiko.ssh_exception.SSHException: EOF during negotiation

    python paramiko模块sftp异常 paramiko ssh exception SSHException EOF during negotiation 参考文章 1 python paramiko模块sftp异常 parami
  • 华为云DevCloud让软件开发更加高效智能, DevOps不止于美

    高速的中子撞击U235原子核 使其分裂成两个原子核 释放出巨大能量 同时产生的几个中子再去撞击其它原子核 形成链式反应 使得核裂变会无限的产生巨大的能量 将高速的中子比喻成人类的创新思想 原子核就是我们目前的业务 当创新思想碰撞当前业务 就
  • 矿 第三次数据结构实验

    问题 A 任意二叉树的层次遍历 题目描述 有若干个节点 每个节点上都有编号 把这些节点随意地构成二叉树 请编程输出该二叉树的层次遍历序列 输入 第一行是n n小于100 表示有n个节点 每个节点按从1到n依次编号 第一行后有n行 每行三个正
  • Burp Suite 功能介绍

    Target 获取目标应用程序的详细信息 Proxy Proxy是BurpSuite最核心的部分 通过拦截 查看和修改所有的请求和响应您的浏览器与目标Web服务器之间传递 Spider Burp Spider 能使你清楚地了解到一个 web
  • ps保存html和图像格式不显示,photoshop保存web格式不能显示该怎样解决

    在photoshop里面处理完图片保存有的时候会选择web格式 可是保存完了发现不能显示怎么解决 尝试了多次还没有找到解决方法的你 可以看看小编为你推荐的关于保存web格式不能显示的解决方法 想必会对你有帮助 photoshop保存web格
  • MySQL的安装配置

    在Windows系统上配置MySQL数据库 您可以按照以下步骤进行 下载安装MySQL 在官方网站上下载MySQL安装程序 并按照提示安装MySQL 建议下载最新版本的MySQL 以获取更好的性能和安全性 安装界面选择服务器端 设置MySQ
  • #pragma once 与 #ifndef...#define...#endif的比较

    C 中防止头文件被多次include 的常见方式有 用 ifndef define endif 宏 ifndef MYHEAD H define MYHEAD H Here is my class endif 这个是C 语言相关 这是C 语
  • WebFlux使用ServerHttpRequest出现找不到构造

    使用ServerHttpRequest异常 异常 java lang IllegalStateException No primary or single public constructor found for interface org
  • 掌握Python的X篇_35_用Python为美女打码_图像库Pillow

    本篇将会介绍python中的一个图像库Pillow 文章目录 1 Pillow与PIL的关系 2 调整大小 3 加滤镜 4 剪裁 5 生成验证码 1 Pillow与PIL的关系 我们在网上搜python的图像库的话 可能搜到的时PIL 实际
  • snipaste使用教程介绍

    snipaste是一个简单但强大的截图工具 但是很多用户下载之后都不知道怎么用 今天小编带来的内容就是snipaste使用方法教程 Win10纯净版 Win10 64位纯净版 Win10纯净精简版系统下载 系统部落 1 打开Snipaste
  • zynq开发,在SDK中自动生成的IP核驱动文件出现错误解决办法

    ZYNQ开发 在SDK中自动生成的IP核驱动文件出现错误解决办法 zynq开发 在SDK中出现IP驱动文件报错 寻找错误原因 对比 为什么出现这种情况 Block Design查看问题 最后 zynq开发 在SDK中出现IP驱动文件报错 报
  • STM32CUBEMX+FreeRTOS开发笔记3-----按键选择任务挂起

    按键初始化 和上次一样 复制上次的串口文件 改成keytask 打开cubemx工程 将PA3设置成中断线3模式 并将标签改为key1 使能中断线3 添加按键任务 生成代码即可 进入工程 查看中断线3函数 找到callback函数 因为中断
  • 《ESP32-Arduino》LVGL之输入设备详解及实例(触摸屏,实体按键,编码器,多功能按键)

    前言 好久没写博客了 一方面是平时着实没有时间 另一方面是知识还是欠缺 实在没啥技术拿得出手 其实更主要的还是懒 最近玩的比较多的就是LVGL了 自己也是做了几个小项目 后续考虑开源 考虑到网上LVGL入门教程还是比较少 特此出来写篇博客