基于esp-idf的arm2d移植

什么是ARM2D

Arm在Github上发布了一个专门针对“全体” Cortex-M处理器的2D图形加速库——Arm-2D
我们可以简单的把这个2D图形加速库理解为是一个专门针对Cortex-M处理器的标准“显卡驱动”。虽然这里的“显卡驱动”只是一个夸张的说法——似乎没有哪个Cortex-M处理器“配得上”所谓的显卡,但其实也并没有差多远——因为根据最新的趋势,随着单片机资源的逐步丰富(较高级的工艺节点正在逐步降价),处理器不仅跑得越来越快、存储器越来越大,而且大量的厂商已经或者正在考虑给Cortex-M处理器配备专属的2D图形加速引擎

以上摘自公众号裸机思维的文章

首先,arm2d是一个2d引擎库,他是纯软件的东西。很多人可能会被它的arm2d名字给误导。分不清arm2d和DMA2D。实际上DMA2D是硬件,arm2d则是一个软件。arm2d的优秀性能,让我瞠目结舌。在裸机思维的文章中,你不难看到诸如M0+内核、25M主频的主控的平台上跑出各种逆天的效果, 这也是它吸引我的原因。虽然arm开发的初衷是服务于自家的硬件,但是不意味着它不能够移植到别的平台。

以下是我摸索并熟悉arm2d的移植过程

移植前的准备

首先,我们是基于esp-idf 5.0的sdk做的移植。那么,第一需要的肯定是安装环境。这里参考官方手册
就不多赘述。

接下来就应该准备一份驱屏的基础代码了。我们准备了一块esp32s3的开发板,其中屏幕使用了st7789的240X240的spi屏幕。

屏幕驱动

我喜欢以esp-iot-solution中的bus和screen为基础写屏幕驱动。bus中提供了诸如:spi i2c 8080 rgb的通讯层封装,而screen则基于bus的封装提供了st7789 ili9341等lcd芯片封装。
这使得驱屏变得异常简单

cp -r esp-idf/example/get-started/sample_project ./
cd sample_project 

新建components文件夹,再复制刚才提到的bus和screen组件放到components文件夹下。接下来开始着手写屏幕驱动代码:

/*** @file arm_math.h* @author cangyu (sky.kirto@qq.com)* @brief * @version 0.1* @date 2024-06-06* * @copyright Copyright (c) 2024, CorAL. All rights reserved.* */#include <stdio.h>
#include <stdlib.h>#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_log.h"#include "screen_driver.h"
#include "esp_log.h"
/* ==================== [Defines] =========================================== */#define BOARD_IO_SPI2_MISO          -1
#define BOARD_IO_SPI2_MOSI          11
#define BOARD_IO_SPI2_SCK           12
#define BOARD_LCD_SPI_CS_PIN        10
#define BOARD_LCD_SPI_DC_PIN        9
#define BOARD_LCD_SPI_RESET_PIN     -1
#define BOARD_LCD_SPI_BL_PIN        46
#define BOARD_LCD_SPI_CLOCK_FREQ    40000000/* ==================== [Typedefs] ========================================== *//* ==================== [Static Prototypes] ================================= */static void screen_clear(scr_driver_t *lcd, int color);/* ==================== [Static Variables] ================================== */static const char *TAG = "screen example";
static scr_driver_t g_lcd;/* ==================== [Macros] ============================================ *//* ==================== [Global Functions] ================================== */void app_main(void)
{spi_config_t bus_conf = {.miso_io_num = BOARD_IO_SPI2_MISO,.mosi_io_num = BOARD_IO_SPI2_MOSI,.sclk_io_num = BOARD_IO_SPI2_SCK,.max_transfer_sz = 1024*10};spi_bus_handle_t spi2_bus_handle = spi_bus_create(SPI2_HOST, &bus_conf);scr_interface_spi_config_t spi_lcd_cfg = {.spi_bus = spi2_bus_handle,.pin_num_cs = BOARD_LCD_SPI_CS_PIN,.pin_num_dc = BOARD_LCD_SPI_DC_PIN,.clk_freq = BOARD_LCD_SPI_CLOCK_FREQ,.swap_data = true,};scr_interface_driver_t *iface_drv;scr_interface_create(SCREEN_IFACE_SPI, &spi_lcd_cfg, &iface_drv);scr_find_driver(SCREEN_CONTROLLER_ST7789, &g_lcd);scr_controller_config_t lcd_cfg = {.interface_drv = iface_drv,.pin_num_rst = BOARD_LCD_SPI_RESET_PIN,.pin_num_bckl = BOARD_LCD_SPI_BL_PIN,.rst_active_level = 0,.bckl_active_level = 1,.offset_hor = 0,.offset_ver = 0,.width = 240,.height = 240,.rotate = SCR_DIR_LRTB,};g_lcd.init(&lcd_cfg);scr_info_t lcd_info;g_lcd.get_info(&lcd_info);ESP_LOGI(TAG, "Screen name:%s | width:%d | height:%d", lcd_info.name,lcd_info.width, lcd_info.height);screen_clear(&g_lcd, COLOR_GREEN);}/* ==================== [Static Functions] ================================== */static void screen_clear(scr_driver_t *lcd, int color)
{scr_info_t lcd_info;lcd->get_info(&lcd_info);uint16_t *buffer = malloc(lcd_info.width * sizeof(uint16_t));for (size_t i = 0; i < lcd_info.width; i++) {buffer[i] = color;}for (int y = 0; y < lcd_info.height; y++) {lcd->draw_bitmap(0, y, lcd_info.width, 1, buffer);}free(buffer);
}

接下来就是编译烧录的事情了

idf.py set-target esp32s3 # 切换芯片
idf.py build 		# 编译代码
idf.py flash 		# 烧录
idf.py monitor      # 显示串口log

这是运行的效果

驱屏成果
如此,我们便得到了一个干净的驱屏的工程。

ARM2D的组件加入

我们在components文件夹下面创建一个arm2d的组件文件夹。再在arm2d文件夹里clone arm2d的仓库

cd  components 		# 进入组件文件夹
mkdir arm2d 		# 创建arm2d组件件夹
cd arm2d			# 进入arm2d组件文件夹
git clone https://github.com/ARM-software/Arm-2D.git # clone arm2d仓库

arm2d的仓库里面很多文件夹,很多文件。我们首要的就是要弄清楚哪些是我们需要的。我们需要的文件主要分布在Library中和Helper中。其中Library是核心部分,而Helper则是后续添加的有帮助的部分。我们在arm2d里面创建一个CMakeLists.txt用于添加编译

touch CMakeLists.txt

CMakeLists.txt内容如下:

idf_component_register(SRC_DIRS "Arm-2D/Library/Source" "Arm-2D/Helper/Source" INCLUDE_DIRS "Arm-2D/Library/Include" "Arm-2D/Helper/Include")

接下来就是退出到工程根目录开始启动上述的编译。
不出所料发生了报错, 找不到arm_2d_cfg.h。那我们在arm2d的文件夹下面添加它
通过搜索这个文件名,我们发现在components/arm2d/Arm-2D/Library/Include/template路径下是有一个同名的config文件。我们把内容复制粘贴过来。大致看一遍配置,值得注意的是,GLCD_CFG_SCEEN_WIDTHGLCD_CFG_SCEEN_HEIGHT 是屏幕的宽和高,别忘记改成我们的屏幕大小:240*240
修改后,别忘记CMakeLists.txt也要修改:

idf_component_register(SRC_DIRS "Arm-2D/Library/Source" "Arm-2D/Helper/Source" INCLUDE_DIRS "." "Arm-2D/Library/Include" "Arm-2D/Helper/Include")

再次进行编译,果然没那么简单。这里告诉我们缺少arm_2d_user_arch_port.h文件。哎,之前的tamplate文件夹里好像有。直接复制过来。再次进行编译。

然后发现缺少arm_math.h文件。这个文件比较棘手,是arm的dsp库。arm2d为了加速图形计算,使用了很多arm的dsp库来加速。我们的esp32s3不是arm架构的根本没法使用。这下只能自己写一个arm_math.h文件,将arm2d内部依赖arm-dsp库的内容提取出来, 并简单的替代。这个过程比较费时费力,需要从报错中找到源头,然后从arm2d中理解。使用math.h进行替换。这里我直接放出我最终的arm_math.h:

/*** @file arm_math.h* @author cangyu (sky.kirto@qq.com)* @brief * @version 0.1* @date 2024-06-06* * @copyright Copyright (c) 2024, CorAL. All rights reserved.* */#ifndef __ARM_MATH_H__
#define __ARM_MATH_H__/* ==================== [Includes] ========================================== */
#include <math.h>#ifdef __cplusplus
extern "C" {
#endif/* ==================== [Defines] =========================================== *//* ==================== [Typedefs] ========================================== */typedef int16_t q15_t;
typedef int32_t q31_t;
typedef int64_t q63_t;/* ==================== [Global Prototypes] ================================= */__STATIC_FORCEINLINE q31_t clip_q63_to_q31(q63_t x)
{return ((q31_t) (x >> 32) != ((q31_t) x >> 31)) ?((0x7FFFFFFF ^ ((q31_t) (x >> 63)))) : (q31_t) x;
}__STATIC_FORCEINLINE float arm_sin_f32(float x)
{return sin(x);
}__STATIC_FORCEINLINE float arm_cos_f32(float x)
{return cos(x);
}__STATIC_FORCEINLINE q31_t arm_sin_q31(q31_t x)
{return (q31_t)sin((float)x);
}__STATIC_FORCEINLINE q31_t arm_cos_q31(q31_t x)
{return (q31_t)cosl((float)x);
}__STATIC_FORCEINLINE uint32_t usat(int32_t val, uint8_t sat) {uint32_t max = (1U << sat) - 1; // 最大值为 2^sat - 1if (val < 0) {return 0;} else if (val > max) {return max;} else {return (uint32_t)val;}
}__STATIC_FORCEINLINE int32_t saturate_to_int32(int64_t value) {if (value > INT32_MAX) {return INT32_MAX;} else if (value < INT32_MIN) {return INT32_MIN;} else {return (int32_t)value;}
}__STATIC_FORCEINLINE int32_t qadd_impl(int32_t x, int32_t y) {int64_t result = (int64_t)x + y; // 将x和y相加return saturate_to_int32(result); // 对结果进行饱和处理
}/* ==================== [Macros] ============================================ */// 计算一个32位整数从最高有效位
#define __CLZ(x) __builtin_clz(x)// 确保一个数值在给定的位宽内   
#define __USAT(val, sat) usat(val, sat)// 它将两个32位有符号整数相加,并在结果超出32位有符号整数范围时进行饱和处理
#define __QADD(x, y) qadd_impl(x, y)#ifdef __cplusplus
} /* extern "C" */
#endif#endif // __ARM_MATH_H__

再度编译发现虽然编译过了,但是很多地方有warning,看着十分难受。这里去请教了arm2d的作者,傻孩子大佬。大致了解了原因后按照他的说法在CMakeLists.txt中加入了两行编译器命令

idf_component_register(SRC_DIRS "Arm-2D/Library/Source" "Arm-2D/Helper/Source" INCLUDE_DIRS "." "Arm-2D/Library/Include" "Arm-2D/Helper/Include")target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-implicit-fallthrough -Wno-unused-variable)
target_compile_options(${COMPONENT_LIB} PRIVATE -fms-extensions)

这样的话编译就没有警告了,nice!

对接arm2d

arm2d的对接十分曲折,由于arm2d的源代码拥有若干个宏堆砌而成。很难读懂,我也是参考了components/arm2d/Arm-2D/examples/[template][pc][vscode]/platform路径下的arm_2d_disp_adapter_0.h和arm_2d_disp_adapter_0.c拉过来放到arm2d文件夹下。
接了这两个文件的代码后,由于引入了.c和一些esp32的代码,其中arm_2d_disp_adapter_0.c的代码还借用了components/arm2d/Arm-2D/examples/common里面的代码。那么CMakeLists.txt自然也要修改如下:

idf_component_register(SRC_DIRS "." "Arm-2D/Library/Source" "Arm-2D/Helper/Source" "Arm-2D/examples/common/controls" "Arm-2D/examples/common/asset"INCLUDE_DIRS "." "Arm-2D/Library/Include" "Arm-2D/Helper/Include" "Arm-2D/examples/common/controls" "Arm-2D/examples/common/asset")target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-implicit-fallthrough -Wno-unused-variable)
target_compile_options(${COMPONENT_LIB} PRIVATE -fms-extensions)

然而编译后,发现arm_2d_disp_adapter_0.c 和 arm_2d_disp_adapter_0.h里面的代码都是灰色,原来是缺少RTE_Acceleration_Arm_2D_Helper_Disp_Adapter0宏,搜索arm2d文件发现在 components/arm2d/Arm-2D/examples/[template][pc][vscode]/platform/RTE_Components.h 中有定义,那么拉取到arm2d的文件夹内后,还需要
修改CMakeLists.txt添加一个编译宏 _RTE_ 就可以了

idf_component_register(SRC_DIRS "." "Library/Source" "Helper/Source" "common/controls" "common/asset" INCLUDE_DIRS "." "Library/Include" "Helper/Include" "common/controls" "common/asset" )target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-implicit-fallthrough -Wno-unused-variable)
target_compile_options(${COMPONENT_LIB} PRIVATE -fms-extensions)
target_compile_definitions(${COMPONENT_LIB} PRIVATE "_RTE_" )

这部分的代码真的很难理解,作者十分擅长用宏。在这样的基础之下,写下的代码如同自带一层混淆,让人难以读懂和移植。
然后我们编译后发现缺少 :

void Disp0_DrawBitmap(uint32_t x, uint32_t y, uint32_t width, uint32_t height, const uint8_t *bitmap)

int64_t arm_2d_helper_get_system_timestamp(void)

uint32_t arm_2d_helper_get_reference_clock_frequency(void)

这部分是对接esp32底层,我们留在main里面做

主函数调用arm2d

到了主函数了,首先我们要引入头文件

// arm2d的内容
#include "arm_2d.h"
#include "arm_2d_disp_adapter_0.h"// 对接需要的内容
#include "esp_timer.h"

然后我们要对接Disp0_DrawBitmap函数,这部分是给arm2d底层刷新屏幕使用

void Disp0_DrawBitmap(uint32_t x, uint32_t y, uint32_t width, uint32_t height, const uint8_t *bitmap)
{g_lcd.draw_bitmap(x, y, width, height, (uint16_t*)bitmap);
}

对接arm_2d_helper_get_system_timestamp函数,这部分给arm2d提供时间戳:

int64_t arm_2d_helper_get_system_timestamp(void)
{return esp_timer_get_time();
}

对接arm_2d_helper_get_reference_clock_frequency函数,这部分是时间戳频率:

uint32_t arm_2d_helper_get_reference_clock_frequency(void)
{return 1000000;
}

然后,主函数下面加入代码:

 arm_irq_safe {arm_2d_init();}disp_adapter0_init(Disp0_DrawBitmap);while (1){disp_adapter0_task();vTaskDelay(1);}

开始编译,结果最后的链接阶段报错:

A fatal error occurred: Segment loaded at 0x3c030390 lands in same 64KB flash mapping as segment loaded at 0x3c030020. Can't generate binary. Suggest changing linker script or ELF to merge sections.
ninja: build stopped: subcommand failed.

通过翻译软件我们知道,这里的段错误,好像是冲突了。
我们通过指令xtensa-esp32-elf-objdump -h build/lcd_tjpgd.elf查找了所有的段:

$ xtensa-esp32-elf-objdump -h build/lcd_tjpgd.elf build/lcd_tjpgd.elf:     file format elf32-xtensa-leSections:
Idx Name          Size      VMA       LMA       File off  Algn0 .rtc.text     00000010  600fe000  600fe000  00054000  2**0ALLOC1 .rtc.force_fast 00000000  600fe010  600fe010  0005374f  2**0CONTENTS2 .rtc_noinit   00000000  50000000  50000000  0005374f  2**0CONTENTS3 .rtc.force_slow 00000000  50000000  50000000  0005374f  2**0CONTENTS4 .rtc_reserved 00000018  600fffe8  600fffe8  00053fe8  2**3ALLOC5 .iram0.vectors 00000403  40374000  40374000  0001d000  2**2CONTENTS, ALLOC, LOAD, READONLY, CODE6 .iram0.text   0000edbb  40374404  40374404  0001d404  2**2CONTENTS, ALLOC, LOAD, READONLY, CODE7 .dram0.dummy  0000b200  3fc88000  3fc88000  0000f000  2**0ALLOC8 .dram0.data   0000255c  3fc93200  3fc93200  0001a200  2**4CONTENTS, ALLOC, LOAD, DATA9 .noinit       00000000  3fc9575c  3fc9575c  0005374f  2**0CONTENTS10 .dram0.bss    00004040  3fc95760  3fc95760  0001c75c  2**3ALLOC11 .flash.text   0002672f  42000020  42000020  0002d020  2**2CONTENTS, ALLOC, LOAD, READONLY, CODE12 .flash_rodata_dummy 00030000  3c000020  3c000020  00001020  2**0ALLOC13 .flash.appdesc 00000100  3c030020  3c030020  00001020  2**4CONTENTS, ALLOC, LOAD, READONLY, DATA14 arm2d.tile.c_tileWhiteDotMask 00000010  3c030120  3c030120  00001120  2**2CONTENTS, ALLOC, LOAD, READONLY, DATA15 arm2d.tile.c_tileWhiteDotRGB565 00000010  3c030130  3c030130  00001130  2**2CONTENTS, ALLOC, LOAD, READONLY, DATA16 arm2d.asset.c_bmpWhiteDotRGB565 00000188  3c030140  3c030140  00001140  2**2CONTENTS, ALLOC, LOAD, READONLY, DATA17 arm2d.asset.c_bmpWhiteDotAlpha 000000c4  3c0302c8  3c0302c8  000012c8  2**2CONTENTS, ALLOC, LOAD, READONLY, DATA18 .flash.rodata 0000d01c  3c030390  3c030390  00001390  2**4CONTENTS, ALLOC, LOAD, DATA19 .flash.rodata_noload 00000000  3c03d3ac  3c03d3ac  0005374f  2**0CONTENTS20 .ext_ram.dummy 0003ffe0  3c000020  3c000020  00001020  2**0ALLOC21 .ext_ram.bss  00000000  3c040000  3c040000  0005374f  2**0CONTENTS22 .iram0.text_end 00000041  403831bf  403831bf  0002c1bf  2**0ALLOC23 .iram0.data   00000000  40383200  40383200  0005374f  2**0CONTENTS24 .iram0.bss    00000000  40383200  40383200  0005374f  2**0CONTENTS25 .dram0.heap_start 00000000  3fc997a0  3fc997a0  0005374f  2**0CONTENTS26 .xt.prop      0002c2f8  00000000  00000000  0005374f  2**0CONTENTS, READONLY27 .xt.lit       000013a0  00000000  00000000  0007fa47  2**0CONTENTS, READONLY28 .xtensa.info  00000038  00000000  00000000  00080de7  2**0CONTENTS, READONLY29 .comment      0000004b  00000000  00000000  00080e1f  2**0CONTENTS, READONLY30 .debug_frame  00013cd8  00000000  00000000  00080e6c  2**2CONTENTS, READONLY, DEBUGGING, OCTETS31 .debug_info   001d8e2e  00000000  00000000  00094b44  2**0CONTENTS, READONLY, DEBUGGING, OCTETS32 .debug_abbrev 00028366  00000000  00000000  0026d972  2**0CONTENTS, READONLY, DEBUGGING, OCTETS33 .debug_loc    000d92c3  00000000  00000000  00295cd8  2**0CONTENTS, READONLY, DEBUGGING, OCTETS34 .debug_aranges 00007910  00000000  00000000  0036efa0  2**3CONTENTS, READONLY, DEBUGGING, OCTETS35 .debug_ranges 0000f150  00000000  00000000  003768b0  2**3CONTENTS, READONLY, DEBUGGING, OCTETS36 .debug_line   00176594  00000000  00000000  00385a00  2**0CONTENTS, READONLY, DEBUGGING, OCTETS37 .debug_str    000474ad  00000000  00000000  004fbf94  2**0CONTENTS, READONLY, DEBUGGING, OCTETS38 .debug_loclists 0000f07c  00000000  00000000  00543441  2**0CONTENTS, READONLY, DEBUGGING, OCTETS39 .debug_rnglists 00000418  00000000  00000000  005524bd  2**0CONTENTS, READONLY, DEBUGGING, OCTETS40 .debug_line_str 00001955  00000000  00000000  005528d5  2**0CONTENTS, READONLY, DEBUGGING, OCTETS

定位了问题出在arm2d里面,好像是arm2d的某个操作导致了内存段覆盖。搜索关键词arm2d.tile,发现很多地方使用了ARM_SECTION(“arm2d.tile.c_tileUTF8UserFontA1Mask”)。知道这里很简单,找到根源然后将它注释,这个宏就不会起作用了。
大约是在components/arm2d/Arm-2D/Library/Include/arm_2d_utils.h文件的620行我找到了这个宏,并在
arm_2d_cfg.h中加入宏定义来替换掉内部的宏:

// 屏蔽内部的段操作
#define ARM_SECTION(__X)

至此,我们编译终于成功。虽然这时候还有一些warning没有消除(arm2d被调用的时候产生的warning)。但是我也无力追求完美了。直接编译,烧录。
结果没有出现想要的动画效果,这里我们通过在arm_2d_cfg.h中打开log分析发现,是因为没有启动arm_2d_disp_adapter_0.h中的默认界面。我们通过arm_2d_disp_adapter_0.h的下面宏:

// <q>Disable the default scene
// <i> Remove the default scene for this display adapter. We highly recommend you to disable the default scene when creating real applications.
#ifndef __DISP0_CFG_DISABLE_DEFAULT_SCENE__
#   define __DISP0_CFG_DISABLE_DEFAULT_SCENE__                     0
#endif

这里默认__DISP0_CFG_DISABLE_DEFAULT_SCENE__ 是1,我们设置成0,打开它。再进行编译烧录(别忘记arm_2d_cfg.h中关闭log)。结果如下:

运行效果

总结

其实我的结果并不是很好。按照傻孩子大佬的话说,还是有很大优化空间。下一步优化应该就是在spi异步传输的方向上。乐鑫的spi分为queue传输和poll传输,其中bus库采用的是poll传输。然而这种方式相当于同步操作,应该用queue去异步等待,这样能在传输的同时计算像素。能够消除LCD-Latency的时间。我这次记录摸索过程相当于是抛砖引玉,希望大家能够优化出更好的版本

关于移植

移植的一个最大的准则就是”不要动别人的源码“。按照傻孩子大佬的说法就是【用扩展替代修改】。我遇到问题,虽然会深入源码,但是会根据源码的情况在配置文件或者是自己写的文件里面进行补充。如果别人的代码让你无法这样操作,那就是提issue的时候。

后记(碎碎念)

arm2d的理念是以mask为中心,所有的东西全都是贴图加上arm2d自带的蒙版(mask)完成的效果。这个理念实际上不是gui的理念,例如lvgl是以控件为中心。arm2d则是更加底层, 从使用者的角度实际上会比较麻烦,但是这种效果能够在性能有限的设备上发挥很大的效果。实际上arm2d的源码一度让我崩溃,以宏构建的内容, 很多情况下无从知晓如何使用。代码的抽象程度已经完全是另一种语言。这次的移植意义也不大,因为esp32性能足够,有更加有好的lvgl,没必要折腾arm2d。主要是想学习学习,也想挑战一下自己。arm2d的代码在我看来我不能评价他是不好的,毕竟恐怖的效率,惊人的效果还是深深折服。但是我个人还是不会学习他的做法,我希望能写出更加清晰易懂的代码。

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

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

相关文章

丝杆支撑座:滚珠丝杆稳定运行的守护者!

丝杆支撑座是丝杆和电机之间连接的重要组成部分&#xff0c;发挥着非常重要的功能。提到丝杆支撑座和滚珠丝杆&#xff0c;很多人都会想到支撑关系&#xff0c;但丝杆支撑座作为滚珠丝杆系统中至关重要的角色&#xff0c;其作用远不止于简单的支撑。 丝杆支撑座安装过程非常简单…

绘唐3是免费的吗?

绘唐科技是一家中国电子信息产品制造商和供应商&#xff0c;成立于2005年。公司主要经营智能硬件、智能穿戴设备、智能家居设备和智能交通设备等领域的产品开发和销售。绘唐科技拥有强大的研发团队和制造能力&#xff0c;能够为客户提供定制化的产品解决方案。 绘唐科技的产品种…

CS-隐藏防朔源-数据转发-iptables(Linux自带的防火墙)

免责声明:本文仅做技术交流与学习... 目录 准备环境: 1-iptables转发机设置转发: 2-CS服务器配置iptables服务器的IP 准备环境: 两台外网服务器. --iptables服务器就是做一个中转...封了中转就没了... 1-iptables转发机设置转发: iptables -I INPUT -p tcp -m tcp --dport 8…

ACC:Automatic ECN Tuning for High-Speed Datacenter Networks 相关知识点介绍(一)

目录 ACC&#xff08;Adaptive Congestion Control&#xff09; 总结 结合 ACC 和 ECN ECN ECN&#xff08;Explicit Congestion Notification&#xff09; 静态 ECN 动态 ECN 对比 总结 FCT——flow completion time 具体解释 小鼠流和大象流 小鼠流&#xff08;…

【最新综述】基于伪标签的半监督语义分割

Semi-Supervised Semantic Segmentation Based on Pseudo-Labels: A Survey 摘要&#xff1a; 语义分割是计算机视觉领域的一个重要而热门的研究领域&#xff0c;其重点是根据图像中像素的语义对其进行分类。然而&#xff0c;有监督的深度学习需要大量数据来训练模型&#xff…

GPT-5的到来:智能飞跃与未来畅想

IT之家6月22日消息&#xff0c;在美国达特茅斯工程学院的采访中&#xff0c;OpenAI首席技术官米拉穆拉蒂确认了GPT-5的发布计划&#xff0c;预计将在一年半后推出。穆拉蒂形象地将GPT-4到GPT-5的飞跃比作高中生到博士生的成长。这一飞跃将给我们带来哪些变化&#xff1f;GPT-5的…

电路笔记(电源模块):TPS82130降压模块

芯片引脚说明 Layer 1 1 2 3 4 5 6 7 8 SS/TR PG FB VOUT EN VIN GND VOUT Thermal Pad 使能引脚&#xff0c;高电平启动。 反馈参考引脚。 连接到该引脚的外部电阻分压器对输出电压进行编程。 电源开漏输出引脚。 软启动和电压跟踪引脚。 上拉电阻可以连接到任何低于6V的电压。…

如何使用WxPusher向个人微信推送发送实时消息,比如定时任务等

wxpusher-sdk-java这个框架开源了&#xff1a;GitHub - wxpusher/wxpusher-sdk-java: 微信消息实时推送服务[WxPusher]的Java版本sdk&#xff0c;可以通过API实时给个人微信推送消息。wechat pusher. 文档地址&#xff1a;WxPusher微信推送服务 WxPusher (微信推送服务)是一个…

湖北大学2024年成人高考函授报名专升本教育学专业介绍

湖北大学&#xff0c;作为一所历史悠久、文化底蕴深厚的学府&#xff0c;其成人高等继续教育体系更是为广大学子提供了一片展翅高飞的蓝天。在这片知识的海洋中&#xff0c;专升本教育学专业如同一颗璀璨的明珠&#xff0c;闪耀着智慧的光芒。 湖北大学的专升本教育学专业&…

Postgresql从小白到高手 九 : psql高级查询及内部视图使用

Postgresql从小白到高手 九:pgsql 复杂查询及内部表高级查询 文章目录 Postgresql从小白到高手 九:pgsql 复杂查询及内部表高级查询一、多表查询二、pgsql内部表1.内部表2.内部表查询应用 一、多表查询 内联 &#xff1a;inner join on 简写 join on 结果集只有符合 筛选条件…

Android U Settings 应用中 APN 菜单实现的代码逻辑

功能简介 MobileNetwork移动网络设置页面下有【接入点设置】(APN)。 问题:为什么Controller初始化找不到pref,然后报错。 Note:什么时候切换成Controller的呢?在Android T&U 上还没有更新成kt实现 ,但是已经有Controller的方案。 流程逻辑 1、界面“telephony_a…

群晖系统百度网盘套件卸载之后无法再次安装 ContainerManager项目无法删除

前言 最近重新组了个NAS&#xff0c;在套件迁移的时候遇到个头疼的问题。在用矿神的百度网盘在迁移的时候出错了&#xff0c;于是我自己删掉baiduapp得容器和镜像然后卸载套件。不知道中间出了啥问题&#xff0c;套件是已经卸载了&#xff0c;但是群晖ContainerManager套件中的…

Adobe Indesign 操作

页面设计 页面设置 版面&#xff1a;图文和空白部分的总和。 版心&#xff1a;规划在版面中排印文本和图片的部分。 开本&#xff1a;单个页面的宽度和高度。 如图所示&#xff0c;新建文件&#xff0c;自定义是210297毫米。这个数据是开本大小。 点击“边距和分栏”&#…

Vue3学习笔记<->创建第一个vue项目

新建一个项目目录 找一个盘新建一个目录&#xff0c;我这里在D盘创建一个vuedemo目录作为项目存放的目录。使用idea打开目录。   单击ieda底部的按钮“Terminal”&#xff0c;打开命令行窗口&#xff0c;如果命令行窗口当前目录不是“vuedemo”&#xff0c;就切换到“vuedem…

抖音营销新策略:MessageBox与HubSpot集成,引领企业获客新潮流

在全球数字化浪潮中&#xff0c;抖音以其独特的短视频形式、庞大的用户群体和高度互动性&#xff0c;成为了企业出海战略中不可或缺的一环。抖音营销不仅仅是简单的内容发布和互动&#xff0c;它更是一种深度策略和创新思维的体现。今天将深入探讨抖音营销的核心价值、应用场景…

了解和解决“vcruntime140_1.dll”相关问题,有效修复vcruntime140_1.dll错误弹窗问题

在Windows操作系统中&#xff0c;经常会遇到各种DLL&#xff08;动态链接库&#xff09;错误&#xff0c;它们是Windows应用运行不可或缺的一部分。其中&#xff0c;“vcruntime140_1.dll”是一个常见的DLL文件&#xff0c;这个文件与Microsoft Visual Studio C 运行时相关联。…

使用Python Selenium,动态网页不再是难题!

目录 1、直接执行JS代码 🌐 1.1 execute_script基础用法 1.2 带参数执行JS函数 1.3 获取执行结果 2、使用execute_async_script异步执行 🔄 2.1 适用场景分析 2.2 实现异步操作示例 2.3 错误处理与调试技巧 3、JS与页面元素交互 👤 3.1 修改DOM属性 3.2 触发事…

C# 23设计模式备忘

创建型模式&#xff1a;单例&#xff08;Singleton&#xff09;模式&#xff1a;某个类只能生成一个实例&#xff0c;该类提供了一个全局访问点供外部获取该实例&#xff0c;其拓展是有限多例模式。 原型&#xff08;Prototype&#xff09;模式&#xff1a;将一个对象作为原型&…

下载和使用SLUN数据集

1. 下载数据集 网址在https://opendatalab.com/OpenDataLab/lsun/tree/main/raw/scenes 下载bedroom_val_lmdb.zip 然后解压后会又两个文件&#xff0c;一个data.mdb&#xff0c;另一个lock.mdb。 2. 使用torchvison使用LSUN数据集 我把解压后的bedroom_val_lmdb放在/home/…

iOS 其他应用的文件如何在分享中使用自己的应用打开

废话少说 一、第一步&#xff1a;先配置好plist文件 右击info.plist如下图文件打开 根据自己需要配置支持的文件类型&#xff0c;也可使用property List中配置&#xff0c;一样的 其他的文件可是参考文档&#xff1a;System-Declared Uniform Type Identifiers 可复制的代码&am…