【GUI】LVGL无操作系统移植以及移植过程错误处理

 目录    

介绍

1. 删除源码

2. 导入lvgl到项目screen_mcu中

3. keil添加分组和头文件

4. 移植显示

5. 移植触摸

6. 添加测试案例

6.1. 测试按钮

6.2. 测试音乐界面

7. 提供时钟

错误处理

L6218E错误

出现花屏

屏幕颜色不对

内存分配


介绍

        本文 主要介绍GD32移植LVGL的过程,以及移植过程中出现的问题以及解决方法,写的非常详细,基本上都截图了。    移植前需要准备下列:

  1. 准备包含触摸功能的屏幕
  2. 实现触摸屏的显示和触摸驱动,确保屏幕没有问题。
  3. 下载lvgl 8.3版本源码下载地址:https://github.com/lvgl/lvgl

本次移植开发板

  • 立创天空星GD32F407VET6
  • 1.69寸电容触摸屏  触摸芯片CS816t,显示:st7789

1. 删除源码

删除源码中不需要的文件夹,仅保留如下内容

  1. demos : lvgl综合案例
  2. examples :单个功能案例
  3. src : 源代码
  4. lv_conf_template.h : 重要的配置文件,里面存在非常多的开关
  5. lvgl.h : 头文件

将上述内容存放到名为lvgl的文件夹中

2. 导入lvgl到项目screen_mcu中

1、在screen_mcu项目中新建third_party文件夹,该文件夹用于存放第三方驱动。

2、将第1个步骤中的lvgl文件夹拷入其中

3、将lvgl文件夹中的lv_conf_tempalate.h修改lv_conf.h

4、把lv_conf.h的条件编译指令#if 0修改成#if 1

3. keil添加分组和头文件

1、使用keil打开screen_mcu项目,添加如下分组

third_party/lvgl/example/porting
third_party/lvgl/src/core
third_party/lvgl/src/draw
third_party/lvgl/src/extra
third_party/lvgl/src/font
third_party/lvgl/src/gpu
third_party/lvgl/src/hal
third_party/lvgl/src/misc
third_party/lvgl/src/widgets

2、添加LVGL相关的.c文件到相应分组,如下:

3、添加头文件路径

4、开启C99模式

4. 移植显示

把lv_port_disp_template.c/h的条件编译指令#if 0修改成#if 1

lv_port_disp_template.h中包含输出设备驱动头文件

#include "st7789.h"

lv_port_disp_template.h中宏定义水平和竖直分辨率(默认横屏)

#define MY_DISP_HOR_RES 240     //水平分辨率
#define MY_DISP_VER_RES 280     //垂直分辨率

修改 lv_port_disp_template.c中 的 lv_port_disp_init 函数

配置图形数据缓冲模式

在lv_port_disp_init函数中选择一种缓冲模式,注释掉其它两种模式

在lv_port_disp_template.c中void disp_init函数添加 屏幕显示驱动初始化函数,这里也就是 ST7789_Init();

/*Initialize your display and the required peripherals.*/
static void disp_init(void)
{/*You code here*/ST7789_Init();
}

在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) {ST7789_Fill(area->x1,area->y1,area->x2,area->y2,(uint16_t*)color_p);}/*IMPORTANT!!!*Inform the graphics library that you are ready with the flushing*/lv_disp_flush_ready(disp_drv);
}

5. 移植触摸

     1、把lv_port_indev_tempalte.c/h的条件编译#if 0修改成# if 1

      2、在lv_port_indev_tempalte.c中裁剪输入设备

  • 只保留touchpad_xxx相关的方法,删除其它方法
  • lv_port_indev_init中只保留touchpad相关的代码
  • 在touchpad_init方法中执行触摸屏初始化CST816T_Init();

(包含驱动)

/*Initialize your touchpad*/
static void touchpad_init(void)
{CST816T_Init();            //触摸屏初始化
}

        3、配置触摸检测函数

/*Return true is the touchpad is pressed*/
static bool touchpad_is_pressed(void)
{/*Your code comes here*/return CST816T_is_pressed();// return false;
}

        4、配置坐标获取函数

/*Get the x and y coordinates if the touchpad is pressed*/
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
{/*Your code comes here*/CST816T_get_xy((uint16_t*)x,(uint16_t*)y);//(*x) = 0;//(*y) = 0;
}

lv_port_indev_template.c修改之后代码如下:

/*** @file lv_port_indev_templ.c**//*Copy this file as "lv_port_indev.c" and set this value to "1" to enable content*/
#if 1/**********************      INCLUDES*********************/
#include "lv_port_indev_template.h"
//#include "../../lvgl.h"
#include "cst816t.h"/**********************      DEFINES*********************//***********************      TYPEDEFS**********************//***********************  STATIC PROTOTYPES**********************/static void touchpad_init(void);
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static bool touchpad_is_pressed(void);
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y);static void mouse_init(void);
static void mouse_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static bool mouse_is_pressed(void);
static void mouse_get_xy(lv_coord_t * x, lv_coord_t * y);static void keypad_init(void);
static void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static uint32_t keypad_get_key(void);static void encoder_init(void);
static void encoder_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static void encoder_handler(void);static void button_init(void);
static void button_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static int8_t button_get_pressed_id(void);
static bool button_is_pressed(uint8_t id);/***********************  STATIC VARIABLES**********************/
lv_indev_t * indev_touchpad;
lv_indev_t * indev_mouse;
lv_indev_t * indev_keypad;
lv_indev_t * indev_encoder;
lv_indev_t * indev_button;static int32_t encoder_diff;
static lv_indev_state_t encoder_state;/***********************      MACROS**********************//***********************   GLOBAL FUNCTIONS**********************/void lv_port_indev_init(void)
{/*** Here you will find example implementation of input devices supported by LittelvGL:*  - Touchpad*  - Mouse (with cursor support)*  - Keypad (supports GUI usage only with key)*  - Encoder (supports GUI usage only with: left, right, push)*  - Button (external buttons to press points on the screen)**  The `..._read()` function are only examples.*  You should shape them according to your hardware*/static lv_indev_drv_t indev_drv;/*------------------* Touchpad* -----------------*//*Initialize your touchpad if you have*/touchpad_init();/*Register a touchpad input device*/lv_indev_drv_init(&indev_drv);indev_drv.type = LV_INDEV_TYPE_POINTER;indev_drv.read_cb = touchpad_read;indev_touchpad = lv_indev_drv_register(&indev_drv);}/***********************   STATIC FUNCTIONS**********************//*------------------* Touchpad* -----------------*//*Initialize your touchpad*/
static void touchpad_init(void)
{/*Your code comes here*/CST816T_Init();
}/*Will be called by the library to read the touchpad*/
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{static lv_coord_t last_x = 0;static lv_coord_t last_y = 0;/*Save the pressed coordinates and the state*/if(touchpad_is_pressed()) {touchpad_get_xy(&last_x, &last_y);data->state = LV_INDEV_STATE_PR;}else {data->state = LV_INDEV_STATE_REL;}/*Set the last pressed coordinates*/data->point.x = last_x;data->point.y = last_y;
}/*Return true is the touchpad is pressed*/
static bool touchpad_is_pressed(void)
{/*Your code comes here*/return CST816T_is_pressed();// return false;
}/*Get the x and y coordinates if the touchpad is pressed*/
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
{/*Your code comes here*/CST816T_get_xy((uint16_t*)x,(uint16_t*)y);//(*x) = 0;//(*y) = 0;
}#else /*Enable this file at the top*//*This dummy typedef exists purely to silence -Wpedantic.*/
typedef int keep_pedantic_happy;
#endif

6. 添加测试案例

初始化LVGL,包含LVGL相关头文件

6.1. 测试按钮

#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include <string.h>
#include "USART.h"
#include "I2C.h"#include "st7789.h"
#include "cst816t.h"#include "lvgl.h"
#include "lv_port_disp.h"
#include "lv_port_indev.h"// 事件回调
void event_handler(lv_event_t* e) {lv_event_code_t code = lv_event_get_code(e);if (code == LV_EVENT_VALUE_CHANGED) {printf("toggled btn\n");}
}
void demo_button_checkable() {// 获取显示图层lv_obj_t* screen = lv_scr_act();// 创建按钮lv_obj_t* btn = lv_btn_create(screen);// 设置按钮尺寸lv_obj_set_size(btn, 120, 50);// 4. 在按钮上创建文本并居中显示lv_obj_t* label = lv_label_create(btn);lv_label_set_text(label, "Toggle");// lv_obj_center(label);lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);// 5. 设置按钮可选中lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE);// 默认选中
//  lv_obj_add_state(btn, LV_STATE_CHECKED);lv_obj_add_event_cb(btn, event_handler, LV_EVENT_VALUE_CHANGED, NULL);// 居中lv_obj_center(btn);
}

主函数


int main(void) {// 配置全局中断分组nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);// 初始化系统嘀嗒定时器systick_config();// 初始化USARTUSART_init();I2C_init();// 1. 初始化LVGLlv_init();// 2.显示屏驱动初始化lv_port_disp_init();// 3. 触摸屏(输入设备要在屏幕初始化之后再init,否则会失效)lv_port_indev_init();demo_button_checkable();while(1) {// 4. 每隔x毫秒调用一次心跳lv_tick_inc(1);// 5. 执行定时任务(屏幕渲染,事件处理)lv_timer_handler();// 休眠1msdelay_1ms(1);}
}

6.2. 测试音乐界面

        音乐界面需要接近1M的内存,天空星内存小了,无法测试,下载进去会报几百个错误,显示内存不足,可以使用梁山派。

        1、keil添加lvgl/demos/music文件夹下所有.c文件

        2、添加相关头文件路径

..\..\third_party\lvgl\demos
..\..\third_party\lvgl\demos\music

        3、修改lv_conf.h中的

#define LV_USE_DEMO_MUSIC 0改为#define LV_USE_DEMO_MUSIC 1

#define LV_FONT_MONTSERRAT_12 0改为#define LV_FONT_MONTSERRAT_12 1

#define LV_FONT_MONTSERRAT_16 0改为#define LV_FONT_MONTSERRAT_16 1

#define LV_USE_DEMO_MUSIC       0
改为
#define LV_USE_DEMO_MUSIC       1#define LV_FONT_MONTSERRAT_12 0
改为
#define LV_FONT_MONTSERRAT_12 1#define LV_FONT_MONTSERRAT_16 0
改为
#define LV_FONT_MONTSERRAT_16 1

main.c主函数

#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "bsp_basic_timer.h"#include "lcd.h"
#include "touch.h"#include "lvgl.h"#include "lv_demo_music.h"
#include "lv_conf.h"
#include "lv_port_disp_template.h"
#include "lv_port_indev_template.h"int main(void)
{nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);  // 优先级分组systick_config();	lv_init();// 这里必须先初始化disp再初始化indevlv_port_disp_init();lv_port_indev_init();	lv_demo_music();while(1){lv_tick_inc(1);lv_timer_handler();delay_1ms(1);}
}

7. 提供时钟

        可以不用定时器时钟,直接放在while循环中,后面的定时器就不需要配置

while(1) {lv_tick_inc(5);lv_timer_handler();delay_1ms(5);
}

        在Hardware下创建timer文件夹,文件夹下定义bsp_basic_timer.h和bsp_basic_timer.c文件,内容如下

#ifndef _BSP_BASIC_TIMER_H
#define _BSP_BASIC_TIMER_H#include "gd32f4xx.h"
#include "systick.h"
#include "stdio.h"
#include "lvgl.h"#define BSP_TIMER_RCU  				RCU_TIMER5            // 定时器时钟
#define BSP_TIMER      				TIMER5                // 定时器
#define BSP_TIMER_IRQ  				TIMER5_DAC_IRQn       // 定时器中断
#define BSP_TIMER_IRQHANDLER  TIMER5_DAC_IRQHandler // 定时器中断服务函数//#define BSP_TIMER_RCU  				RCU_TIMER2					// 定时器时钟
//#define BSP_TIMER      				TIMER2							// 定时器
//#define BSP_TIMER_IRQ  			  TIMER2_IRQn					// 定时器中断
//#define BSP_TIMER_IRQHANDLER  TIMER2_IRQHandler		// 定时器中断服务函数void basic_timer_config(uint16_t pre,uint16_t per); // 基本定时器配置#endif  /* BSP_BASIC_TIMER_H */

#include "bsp_basic_timer.h"
//#include "bsp_led.h"/************************************************
函数名称 : basic_timer_config
功    能 : 基本定时器配置
参    数 : pre:时钟预分频值per:周期 
*************************************************/
void basic_timer_config(uint16_t pre,uint16_t per)
{/* 一个周期的时间T = 1/f, 定时时间time = T * 周期设预分频值位pre,周期位pertime = (pre + 1) * (per + 1) / psc_clk*/timer_parameter_struct timere_initpara; 							// 定义定时器结构体/* 开启时钟 */rcu_periph_clock_enable(BSP_TIMER_RCU); 							// 开启定时器时钟/* CK_TIMERx = 4 x CK_APB1  = 4x50M = 200MHZ */rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4); // 配置定时器时钟timer_deinit(BSP_TIMER);														  // 复位定时器/* 配置定时器参数 */timere_initpara.prescaler = pre-1;                    //  时钟预分频值 0-65535   psc_clk = CK_TIMER / pretimere_initpara.alignedmode = TIMER_COUNTER_EDGE;     // 边缘对齐                  timere_initpara.counterdirection = TIMER_COUNTER_UP;  // 向上计数    timere_initpara.period = per-1;                       // 周期  /* 在输入捕获的时候使用  数字滤波器使用的采样频率之间的分频比例 */timere_initpara.clockdivision = TIMER_CKDIV_DIV1;     // 分频因子         /* 只有高级定时器才有 配置为x,就重复x+1次进入中断 */    timere_initpara.repetitioncounter = 0;							  // 重复计数器 0-255  timer_init(BSP_TIMER,&timere_initpara);								// 初始化定时器/* 配置中断优先级 */nvic_irq_enable(BSP_TIMER_IRQ,3,2); 									// 设置中断优先级为 3,2/* 使能中断 */timer_interrupt_enable(BSP_TIMER,TIMER_INT_UP);       // 使能更新事件中断 /* 使能定时器 */timer_enable(BSP_TIMER);
}/************************************************
函数名称 : BSP_TIMER_IRQHandler
功    能 : 基本定时器中断服务函数 
参    数 : 无
返 回 值 : 无
作    者 : LC
*************************************************/
void BSP_TIMER_IRQHANDLER(void)
{/* 这里是定时器中断 */if(timer_interrupt_flag_get(BSP_TIMER,TIMER_INT_FLAG_UP) == SET){timer_interrupt_flag_clear(BSP_TIMER,TIMER_INT_FLAG_UP);  // 清除中断标志位 /* 执行功能 */lv_tick_inc(1);}
}

定义之后,keil添加.c文件和头文件即可

错误处理

L6218E错误

.\Objects\GD32F450.axf: Error: L6218E: Undefined symbol __aeabi_assert (referred from qrcodegen.o).

解决方案:

添加NDEBUG宏定义

设置如下即可

出现花屏

        出现该现象是由于LVGL相对占用内存较大,需要修改内存问题,在startup_gd32f407_427.s中修改内存大小即可

原始:
Stack_Size      EQU     0x00000400
修改后:
Stack_Size      EQU     0x00000800

屏幕颜色不对

        正常颜色应该和下发模拟器图片一致,按钮是蓝色的,出现该问题的原因:LVGL配置的color depth颜色深度参数和屏幕不一致导致,在配置文件lv_conf.h里面修改LV_COLOR_DEPTH、LV_COLOR_16_SWAP的宏定义。

内存分配

         在LVGL中,除了注释不使用相关业务外,有3个位置可以实现修改内存修改,根据自己情况进行来。

1、startup_gd32f407_427.s中修改内存大小,当移植出现花屏的时候,修改这里即可解决。

2、在配置文件lv_conf.h中:LV_MEM_SIZE定义LVGL内部使用的内存池的大小。该内存池用于LVGL的对象、样式、动画等动态分配的内存需求。决定了LVGL可以使用的总内存量

3、显示缓存(Display Buffer)

在配置文件lv_conf.h中,lv_disp_draw_buf_init函数中指定了显示的缓存大小(即MY_DISP_HOR_RES * MY_DISP_VER_RES)直接决定了一次可以绘制的最大像素数量。

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

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

相关文章

BlockingQueue详解(含动画演示)

目录 BlockingQueue详解0、BlockingQueue简介BlockingQueue接口中方法注释BlockingQueue的实现&#xff0c;总结计划 1、ArrayBlockingQueue简介2、ArrayBlockingQueue的继承体系3、ArrayBlockingQueue的构造方法①、 ArrayBlockingQueue(int capacity)②、ArrayBlockingQueue(…

计算机组成原理 —— 存储系统(概述)

计算机组成原理 —— 存储系统&#xff08;概述&#xff09; 存储系统按层次划分按照存储介质分类按照存储方式分类按照信息可更改性分类根据信息的可保存性分类存储器性能指标 我们今天来学习计算机组成原理中的存储系统&#xff1a; 存储系统 存储系统是计算机系统中用于存…

vue实现的商品列表网页

一、商品列表效果如下 二、代码&#xff1b; vue实现的商品列表网页 &#xff0c; 图片在vue项目的Public文件夹里的 imgs中 <template><div class"common-layout"><!-- el-container:外层容器。 当子元素中包含 <el-header> 或 <el-foo…

mysql:简单理解mysql mvcc的可重复读

# 原理 假设有这样的sql begin select&#xff08;或update、insert、delete&#xff09; ... commit当执行【begin】的时候&#xff0c;标记有一个新事务要开始&#xff0c;但是事务还没有真正开始&#xff0c;事务id还没有产生当执行事务里面的第一个sql语句时&#xff08;…

java之url任意跳转漏洞

1 漏洞介绍 URLRedirect url重定向漏洞也称url任意跳转漏洞&#xff0c;网站信任了用户的输入导致恶意攻击&#xff0c;url重定向主要用来钓鱼&#xff0c;比如url跳转中最常见的跳转在登陆口&#xff0c;支付口&#xff0c;也就是一旦登陆将会跳转任意自己构造的网站&#xf…

Xshell7免费版下载安装使用

​一、下载安装​ 1.打开官网下载 https://www.xshell.com/zh/free-for-home-school/ 2.选择合适的下载路径&#xff0c;点击下载按钮&#xff0c;然后按照提示完成安装。 二、Xshell7的使用&#xff0c;Xhell连接Linux 1.连接之前&#xff0c;确保在Linux中开启SSH。参考&a…

YOLOv8中的C2f模块

文章目录 一、结构概述二、模块功能 一、结构概述 C2f块:首先由一个卷积块(Conv)组成&#xff0c;该卷积块接收输入特征图并生成中间特征图特征图拆分:生成的中间特征图被拆分成两部分&#xff0c;一部分直接传递到最终的Concat块&#xff0c;另一部分传递到多个Botleneck块进…

QT基础 - 文本文件读写

目录 零. 前言 一.读取文件 二. 写入文件 三. 和二进制读写的区别 零. 前言 在 Qt 中&#xff0c;对文本文件进行读写操作是常见的任务之一。这对于保存和加载配置信息、处理数据文件等非常有用。 Qt 提供了多种方式来读写文本文件&#xff0c;使得文件操作变得相对简单和…

SwiftUI 6.0(iOS 18)ScrollView 全新的滚动位置(ScrollPosition)揭秘

概览 在只有方寸之间大小的手持设备上要想体面的向用户展示海量信息&#xff0c;滚动视图&#xff08;ScrollView&#xff09;无疑是绝佳的“东牀之选”。 在 SwiftUI 历史的长河中&#xff0c;总觉得苹果对于 ScrollView 视图功能的升级是在“挤牙膏”。这不&#xff0c;在本…

spire.Pdf 将pdf转成image

一、nuget安装 <ItemGroup><PackageReference Include"Spire.PDF" Version"10.6.7" /></ItemGroup> 二、直接上代码 using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using System; using System.IO;namespace …

乱弹篇(35)掩耳盗铃与两三十年

成语“ 掩耳盗铃 ”&#xff0c;比喻自己欺骗自己&#xff0c;明明是掩盖不住的事情偏要想法子掩盖。它多用来讽刺那些做事不想让别人知道&#xff0c;却偏偏又引起他人注意的人。 现在网络上以新浪微博和邪恶的“800727”为典型的自媒体平台和其掌控者&#xff0c;就是现代版…

提升研发效率:三品PLM解决方案在汽车汽配行业的实践

随着全球汽车市场的快速发展&#xff0c;中国汽车汽配行业迎来了前所未有的发展机遇。然而&#xff0c;在这一过程中&#xff0c;企业也面临着诸多挑战&#xff0c;如研发能力的提升、技术资料管理的复杂性、以及跨部门协作的困难等。为了应对这些挑战&#xff0c;三品产品生命…

模式分解算法-满足3NF的无损且保持函数依赖的分解算法、满足BCNF的无损连接分解算法

一、引言 1、对指定的关系模式&#xff0c;若范式级别较低&#xff0c;为第一范式或第二范式&#xff0c;由于存在数据冗余或更新异常问题&#xff0c;在实际中一般是不可用的&#xff0c;关系模式的规范化就是将满足低一级的关系模式分解为若干满足高一级范式的关系模式的集合…

Python轻松设置Excel单元格数字显示格式

Excel作为强大的数据处理与分析工具&#xff0c;不仅能够存储大量数据&#xff0c;还支持复杂的数据处理与可视化功能。而如何恰当地展示Excel表格中的数据是Excel文件制作的关键之一。这便涉及到Excel单元格数字格式的设置。数字格式不仅关乎数据的美学呈现&#xff0c;如货币…

聊聊 C# dynamic 类型,并分享一个将 dynamic 类型变量转为其它类型的技巧和实例

前言 dynamic 是一种有别于传统变量类型的动态类型声明&#xff0c;刚开始接触可能在理解上会有些困难&#xff0c;可以简单地把它理解为一个盲盒&#xff0c;你可以任意猜测盒子有什么东西&#xff0c;并认为这些东西真正存在而进行处理&#xff0c;等到真正打开时&#xff0…

网上的流量卡真的可以免费领取吗?

网上的流量卡真的可以免费领取吗&#xff1f;当然可以&#xff0c;目前运营商推出的流量卡都是可以免费领取的。 有很多朋友私信给小编&#xff0c;听说流量卡是免费领取的就觉得不太靠谱&#xff0c;其实这种想法是不对的&#xff0c;首先大家要换位思考一下&#xff0c;如果我…

Ubuntu配置ssh+vnc(完整版)

Ubuntu配置sshvnc&#xff08;完整版&#xff09; 1 配置ssh 1. 安装openssh-server&#xff0c;配置开机自启 # 更新包 sudo apt-get update # 安装openssh-server sudo apt-get install -y openssh-server # 启动服务 sudo service ssh start # 配置开机自启 sudo systemc…

细说MCU定时器模块的输入捕捉功能的实现方法

目录 一、工程背景 二、建立工程 1、配置GPIO 2、选择时钟源和Debug 3、 配置定时器TIM1 4、 配置定时器TIM13 5、配置串口 6、配置中断 7、配置系统时钟 三、代码修改 1、使能TIM1输入捕捉功能和TIM3的PWM输出功能 2、自定义变量 3、重定义回调函数 4、输出到…

【国际化I18n使用方法】vue2使用i18简单实现多语种切换,刷新保持,动态数据处理

效果图 使用流程 总结就是&#xff0c;安装好插件后&#xff0c;配置几个语言的js文件&#xff0c;每个词都要在每个js内写一遍对应的语言&#xff0c;然后通过切换js文件拿到对应的语言&#xff0c;实现翻译的效果。然后当前使用什么语言保存到本地&#xff0c;这样刷新就可以…

【进阶篇-Day4:使用JAVA编写石头迷阵游戏】

目录 1、绘制界面2、打乱石头方块3、移动业务4、游戏判定胜利5、统计步数6、重新游戏7、完整代码&#xff1a; 1、绘制界面 上述思路是&#xff1a;使用一个二维数组存放图片的编号&#xff0c;然后在后持遍历即可获取对应的图片。 代码如下&#xff1a; package com.itheima.s…