OpenHarmony-4.基于dayu800 GPIO 实践(2)
- 基于dayu800 GPIO 进行开发
1.DAYU800开发板硬件接口
LicheePi 4A 板载 2x10pin 插针,其中有 16 个原生 IO,包括 6 个普通 IO,3 对串口,一个 SPI。TH1520 SOC 具有4个GPIO bank,每个bank最大有32个IO:

以俯视底板正面为视角,TOP为左侧,BOTTOM为右侧,GPIO对应关系如下:

GPIO 号的对应关系如下图所示:

从以上可以看出板载20pin插针中,4个普通GPIO对应的数字分别如下表:

1.2.搭建点灯环境
给GPIO接灯,使用HiSpark_WiFi_IoT_SSL_VER.A红绿灯板,将红绿灯板和DAYU800开发版按以下方式接线:

GPIO进行相应的操作:
#黄灯
echo 428 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio428/direction
echo 1 > /sys/class/gpio/gpio428/value
echo 0 > /sys/class/gpio/gpio428/value#红灯
echo 429 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio429/direction
echo 1 > /sys/class/gpio/gpio429/value
echo 0 > /sys/class/gpio/gpio429/value#绿灯
echo 430 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio430/direction
echo 1 > /sys/class/gpio/gpio430/value
2.GPIO 代码框架
- vendor/hihope/dayu800/hdf_config/khdf/device_info/device_info.hcs
32 platform :: host {33 hostName = "platform_host";34 priority = 50;35 device_gpio :: device {36 device0 :: deviceNode {37 policy = 0;38 priority = 10;39 permission = 0644;40 moduleName = "linux_gpio_adapter";41 deviceMatchAttr = "linux_gpio_adapter";42 }43 }
- driver/hdf_core/adapter/khdf/linux/platform/gpio/gpio_adapter.c:
269 static int32_t LinuxGpioInit(struct HdfDeviceObject *device)
270 {
271 if (device == NULL) {
272 HDF_LOGE("%s: Fail, device is NULL.", __func__);
273 return HDF_ERR_INVALID_OBJECT;
274 }
275
276 (void)gpiochip_find(device, LinuxGpioMatchProbe);
277 HDF_LOGI("%s: dev service:%s init done!", __func__, HdfDeviceGetServiceName(device));
278 return HDF_SUCCESS;
279 }319 struct HdfDriverEntry g_gpioLinuxDriverEntry = {
320 .moduleVersion = 1,
321 .Bind = LinuxGpioBind,
322 .Init = LinuxGpioInit,
323 .Release = LinuxGpioRelease,
324 .moduleName = "linux_gpio_adapter",
325 };
326 HDF_INIT(g_gpioLinuxDriverEntry);
根据moduleName = "linux_gpio_adapter"进行配置之后,加载gpio hdf 驱动,调用LinuxGpioInit进行初始化,其中调用linux 内核gpiochip_find函数 遍历GPIO设备(gpio_devices)获取gpio控制器(gpio_chip).
232 static int LinuxGpioMatchProbe(struct gpio_chip *chip, void *data)
233 {
234 int32_t ret;
235 struct GpioCntlr *cntlr = NULL;
236
241 HDF_LOGI("%s: find gpio chip(start:%d, count:%u)", __func__, chip->base, chip->ngpio);
246
247 cntlr = (struct GpioCntlr *)OsalMemCalloc(sizeof(*cntlr));
252
253 cntlr->ops = &g_method; //gpio 操作集
254 cntlr->start = (uint16_t)chip->base;
255 cntlr->count = (uint16_t)chip->ngpio;
256 ret = GpioCntlrAdd(cntlr);
263
264 HDF_LOGI("%s: add gpio controller(start:%d, count:%u) succeed",
265 __func__, cntlr->start, cntlr->count);
266 return 0; // return 0 to continue
267 }215 static struct GpioMethod g_method = {
216 .write = LinuxGpioWrite,
217 .read = LinuxGpioRead,
218 .setDir = LinuxGpioSetDir,
219 .getDir = LinuxGpioGetDir,
220 .setIrq = LinuxGpioSetIrq,
221 .unsetIrq = LinuxGpioUnsetIrq,
222 .enableIrq = LinuxGpioEnableIrq,
223 .disableIrq = LinuxGpioDisableIrq,
224 };
3.调试GPIO代码
3.1.代码目录结构
vendor/hihope/dayu800目录下新建sample目录,目录结构如下:
dayu800/dayu800-sig/vendor/hihope/dayu800/sample$ tree
.
├── BUILD.gn
└── hardware├── BUILD.gn└── gpio├── BUILD.gn├── gpio_dayu800.c├── gpio_dayu800.h└── main.c2 directories, 6 files
3.2.增加编译sample
创建模块目录//vendor/hihope/dayu800/sample,在vendor/hihope/dayu800/ohos.build module_list中添加 模块名称dayu800_sample,如下所示:
diff --git a/dayu800/ohos.build b/dayu800/ohos.build
index cea86ba..d268f2a 100644
--- a/dayu800/ohos.build
+++ b/dayu800/ohos.build
@@ -7,7 +7,8 @@"//vendor/hihope/dayu800/preinstall-config:preinstall-config","//vendor/hihope/dayu800/resourceschedule:resourceschedule","//vendor/hihope/dayu800/etc:product_etc_conf",
- "//vendor/hihope/dayu800/audio:audio_policy_config"
+ "//vendor/hihope/dayu800/audio:audio_policy_config",
+ "//vendor/hihope/dayu800/sample:dayu800_sample"]}},
3.3.新增sample目录BUILD.gn
添加vendor/hihope/dayu800/sample/BUILD.gn文件,内容如下:
1 import("//build/ohos.gni")23 group("dayu800_sample") {4 deps = [5 "hardware:hardware"6 ]7 }
创建名为"dayu800_sample"的group。group的作用是将多个target(可以是源文件、库文件或可执行文件等)组织在一起,方便进行编译和管理。在这个group中,依赖名为"hardware:hardware"的target。这个依赖关系意味着在编译"dayu800_sample"时,需要先编译并链接"hardware:hardware"这个target。通过使用group,可以更方便地管理项目的编译和构建过程。
3.4.新增sample/hardware目录下的BUILD.gn
新建hardware目录,并添加vendor/hihope/dayu800/sample/hardware/BUILD.gn文件:
1 import("//build/ohos.gni")23 group("hardware") {4 deps = [5 "gpio:gpio_dayu800"6 ]7 }
创建名为"hardware"的组(group)。该组依赖于名为"gpio:gpio_dayu800"的依赖项。这个函数的作用是将"hardware"组与"gpio:gpio_dayu800"依赖项相关联。
3.5.新增sample/hardware/gpio目录下的BUILD.gn
新建gpio目录,并添加vendor/hihope/dayu800/sample/hardware/gpio/BUILD.gn文件,输入以下内容:
1 import("//build/ohos.gni")2 import("//build/ohos/ndk/ndk.gni")34 ohos_executable("gpio_dayu800") {5 sources = [6 "main.c",7 "gpio_dayu800.c"8 ]910 include_dirs = [ "//commonlibrary/c_utils/base/include" ]1112 external_deps = [13 "c_utils:utils",14 "hilog_native:libhilog",15 ]1617 install_images = [ "system" ]18 part_name = "product_dayu800"19 }
定义名为gpio_dayu800的可执行文件目标。该目标包含了两个源文件main.c和gpio_dayu800.c,并指定了包含目录//commonlibrary/c_utils/base/include。该目标依赖于外部库c_utils:utils和hilog_native:libhilog。最后,它指定了将生成的可执行文件安装到system镜像,并将该目标归属于product_dayu800部分。
3.6.新增sample/hardware/gpio目录下文件gpio_dayu800.c和gpio_dayu800.h
创建vendor/hihope/dayu800/sample/hardware/gpio/gpio_dayu800.c文件,内容如下:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include "hilog/log.h"
#include "securec.h"
#include "gpio_dayu800.h"int DAYU800_GPIO_Export(int gpioNum, int bExport)
{int ret = -1;char buffer[256] = {0};if (bExport) {(void) snprintf_s(buffer, sizeof(buffer), sizeof(buffer), "echo %d > %s", gpioNum, DAYU800_GPIO_EXPORT);} else {(void) snprintf_s(buffer, sizeof(buffer), sizeof(buffer), "echo %d > %s", gpioNum, DAYU800_GPIO_UNEXPORT);}sighandler_t old_handler;old_handler = signal(SIGCHLD, SIG_DFL);ret = system(buffer);if (ret < 0) {HILOG_ERROR(LOG_CORE, "set gpio%{public}d %{public}s failed", gpioNum, bExport == 1 ? "export" : "unexport");return DAYU800_GPIO_ERR;}(void) signal(SIGCHLD, old_handler);return ret;
}int DAYU800_GPIO_SetDirection(int gpioNum, int direction)
{int ret_sprintf_s = -1;// check gpio export or notchar gpio_file_name[128];(void) memset_s(gpio_file_name, sizeof(gpio_file_name), 0, sizeof(gpio_file_name));ret_sprintf_s = snprintf_s(gpio_file_name, sizeof(gpio_file_name), sizeof(gpio_file_name), "%s%d/direction",DAYU800_GPIO_PEX, gpioNum);if (ret_sprintf_s != 0) {}if (access(gpio_file_name, F_OK) != 0) {HILOG_ERROR(LOG_CORE, "gpio%{public}d not export", gpioNum);return DAYU800_GPIO_NOT_EXPROT_ERROR;}// set gpio directionFILE *fp = NULL;fp = fopen(gpio_file_name, "r+");if (fp == NULL) {HILOG_ERROR(LOG_CORE, "open %{public}s%{public}d/direction failed", DAYU800_GPIO_PEX, gpioNum);return DAYU800_GPIO_ERR;}if (direction == DAYU800_GPIO_DIRECTION_IN) {fprintf(fp, "%s", "in");} else if (direction == DAYU800_GPIO_DIRECTION_OUT) {fprintf(fp, "%s", "out");}(void) fclose(fp);fp = NULL;return 0;
}int DAYU800_GPIO_SetValue(int gpioNum, int value)
{int ret_sprintf_s = -1;// check gpio export or notchar gpio_file_name[128];(void) memset_s(gpio_file_name, sizeof(gpio_file_name), 0, sizeof(gpio_file_name));ret_sprintf_s = snprintf_s(gpio_file_name, sizeof(gpio_file_name), sizeof(gpio_file_name), "%s%d/value",DAYU800_GPIO_PEX, gpioNum);if (ret_sprintf_s != 0) {}if (access(gpio_file_name, F_OK) != 0) {HILOG_ERROR(LOG_CORE, "gpio%{public}d not export", gpioNum);return DAYU800_GPIO_NOT_EXPROT_ERROR;}// set gpio valueFILE *fp = NULL;fp = fopen(gpio_file_name, "r+");if (fp == NULL) {HILOG_ERROR(LOG_CORE, "open %{public}s%{public}d/value failed", DAYU800_GPIO_PEX, gpioNum);return DAYU800_GPIO_ERR;}if (value == DAYU800_GPIO_LOW_LEVE) {fprintf(fp, "%s", "0");} else if (value == DAYU800_GPIO_HIGH_LEVE) {fprintf(fp, "%s", "1");}(void) fclose(fp);fp = NULL;return 0;
}int DAYU800_GPIO_IsExport(int gpioNum, int *value)
{int ret_sprintf_s = -1;if (value == NULL) {return DAYU800_GPIO_ERR;}// check gpio export or notchar gpio_file_name[128];(void) memset_s(gpio_file_name, sizeof(gpio_file_name), 0, sizeof(gpio_file_name));ret_sprintf_s = snprintf_s(gpio_file_name, sizeof(gpio_file_name), sizeof(gpio_file_name), "%s%d/value",DAYU800_GPIO_PEX, gpioNum);if (ret_sprintf_s != 0) {}if (access(gpio_file_name, F_OK) != 0) {HILOG_INFO(LOG_CORE, "gpio%{public}d not export", gpioNum);*value = DAYU800_GPIO_NOT_EXPORT;} else {*value = DAYU800_GPIO_EXPORTED;}return 0;
}int DAYU800_GPIO_GetDirection(int gpioNum, int *value)
{int ret = 0;int ret_sprintf_s = -1;if (value == NULL) {return DAYU800_GPIO_ERR;}// check gpio export or notchar gpio_file_name[128];(void) memset_s(gpio_file_name, sizeof(gpio_file_name), 0, sizeof(gpio_file_name));ret_sprintf_s = snprintf_s(gpio_file_name, sizeof(gpio_file_name), sizeof(gpio_file_name), "%s%d/direction",DAYU800_GPIO_PEX, gpioNum);if (ret_sprintf_s != 0) {}if (access(gpio_file_name, F_OK) != 0) {HILOG_ERROR(LOG_CORE, "gpio%{public}d not export", gpioNum);return DAYU800_GPIO_NOT_EXPROT_ERROR;}// get gpio directionFILE *fp = NULL;char buffer[20] = {0};fp = fopen(gpio_file_name, "r");if (fp == NULL) {HILOG_ERROR(LOG_CORE, "read %{public}s%{public}d/direction failed", DAYU800_GPIO_PEX, gpioNum);return DAYU800_GPIO_ERR;}(void) fread(buffer, sizeof(buffer), 1, fp);(void) fclose(fp);fp = NULL;if (strstr(buffer, "out") != NULL) {*value = DAYU800_GPIO_DIRECTION_OUT;} else if (strstr(buffer, "in") != NULL) {*value = DAYU800_GPIO_DIRECTION_IN;} else {ret = DAYU800_GPIO_ERR;}return ret;
}int DAYU800_GPIO_GetValue(int gpioNum, int *value)
{int ret = 0;int ret_sprintf_s = -1;if (value == NULL) {return DAYU800_GPIO_ERR;}// check gpio export or notchar gpio_file_name[128];(void) memset_s(gpio_file_name, sizeof(gpio_file_name), 0, sizeof(gpio_file_name));ret_sprintf_s = snprintf_s(gpio_file_name, sizeof(gpio_file_name), sizeof(gpio_file_name), "%s%d/value",DAYU800_GPIO_PEX, gpioNum);if (ret_sprintf_s != 0) {}if (access(gpio_file_name, F_OK) != 0) {HILOG_ERROR(LOG_CORE, "gpio%{public}d not export", gpioNum);return DAYU800_GPIO_NOT_EXPROT_ERROR;}// get gpio valueFILE *fp = NULL;char buffer[20] = {0};fp = fopen(gpio_file_name, "r");if (fp == NULL) {HILOG_ERROR(LOG_CORE, "read %{public}s%{public}d/value failed", DAYU800_GPIO_PEX, gpioNum);return DAYU800_GPIO_ERR;}(void) fread(buffer, sizeof(buffer), 1, fp);(void) fclose(fp);fp = NULL;if (strstr(buffer, "0") != NULL) {*value = DAYU800_GPIO_LOW_LEVE;} else if (strstr(buffer, "1") != NULL) {*value = DAYU800_GPIO_HIGH_LEVE;} else {ret = DAYU800_GPIO_ERR;}return ret;
}
创建vendor/hihope/dayu800/sample/hardware/gpio/gpio_dayu800.h文件,内容如下:
#ifndef __DAYU800_GPIO_H__
#define __DAYU800_GPIO_H__#define DAYU800_GPIO_EXPORT "/sys/class/gpio/export"
#define DAYU800_GPIO_UNEXPORT "/sys/class/gpio/unexport"
#define DAYU800_GPIO_PEX "/sys/class/gpio/gpio"// hilog
#undef LOG_DOMAIN
#undef LOG_TAG
#define LOG_DOMAIN 0
#define LOG_TAG "GPIO_DAYU800"// gpios
#define DAYU800_GPI0_1_3 427 /* IO1_3 */
#define DAYU800_GPI0_1_4 428 /* IO1_4 */
#define DAYU800_GPI0_1_5 429 /* IO1_5 */
#define DAYU800_GPI0_1_6 430 /* IO1_6 */// direction
#define DAYU800_GPIO_DIRECTION_IN 0
#define DAYU800_GPIO_DIRECTION_OUT 1// is export
#define DAYU800_GPIO_NOT_EXPORT 0
#define DAYU800_GPIO_EXPORTED 1// errno
#define DAYU800_GPIO_ERR (-1)
#define DAYU800_GPIO_NOT_EXPROT_ERROR (-2)// value high - low level
#define DAYU800_GPIO_LOW_LEVE 0
#define DAYU800_GPIO_HIGH_LEVE 1/*** set gpio export* @param gpioNum gpioNum* @param bExport export,0:not export 1:export*/
int DAYU800_GPIO_Export(int gpioNum, int bExport);/*** set gpio direction* @param gpioNum gpioNum* @param direction direction,0:in 1:out*/
int DAYU800_GPIO_SetDirection(int gpioNum, int direction);/*** set gpio value* @param gpioNum gpioNum* @param value value,0:low 1:high*/
int DAYU800_GPIO_SetValue(int gpioNum, int value);/*** check gpio export or not* @param gpioNum gpioNum* @param *value export,0:not export 1:exported*/
int DAYU800_GPIO_IsExport(int gpioNum, int *value);/*** get gpio direction* @param gpioNum gpioNum* @param *value direction,0:in 1:out*/
int DAYU800_GPIO_GetDirection(int gpioNum, int *value);/*** get gpio value* @param gpioNum gpioNum* @param *value value,0:low 1:high*/
int DAYU800_GPIO_GetValue(int gpioNum, int *value);#endif /* __DAYU800_GPIO_H__ */
以上函数提供了对Dayu800开发板GPIO的控制,包括导出、设置方向、设置值、检查导出状态、获取方向和获取值等操作。用于管理Dayu800GPIO的C函数库。以下是每个函数的功能解释:
-
DAYU800_GPIO_Export 函数用于导出或取消导出GPIO。根据输入的bExport参数,函数将构建一个命令行字符串来执行导出或取消导出操作。如果操作成功,函数返回0,否则返回错误代码。
-
DAYU800_GPIO_SetDirection 函数用于设置GPIO的方向。首先,函数检查GPIO是否已导出。然后,它打开GPIO的方向文件,并根据输入的direction参数设置为输入或输出。
-
DAYU800_GPIO_SetValue 函数用于设置GPIO的值。首先,函数检查GPIO是否已导出。然后,它打开GPIO的值文件,并根据输入的value参数设置为低电平或高电平。
-
DAYU800_GPIO_IsExport 函数用于检查GPIO是否已导出。它构建GPIO的值文件路径并检查该文件是否存在。根据检查结果,函数通过value参数返回导出状态。
-
DAYU800_GPIO_GetDirection 函数用于获取GPIO的方向。首先,函数检查GPIO是否已导出。然后,它打开GPIO的方向文件并读取方向值。根据读取的结果,函数通过value参数返回方向值。
-
DAYU800_GPIO_GetValue 函数用于获取GPIO的值。首先,函数检查GPIO是否已导出。然后,它打开GPIO的值文件并读取值。根据读取的结果,函数通过value参数返回值。
3.7.新增sample/hardware/gpio目录下的main.c
创建vendor/hihope/dayu800/sample/hardware/gpio/main.c文件,输入以下内容:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>#include "gpio_dayu800.h"int main(int argc, char **argv)
{int gpioNum = DAYU800_GPI0_1_3;int bExport = DAYU800_GPIO_EXPORTED;int direction = DAYU800_GPIO_DIRECTION_OUT;int value = DAYU800_GPIO_HIGH_LEVE;int getValue = -1;// 检查参数数量以确保至少提供了预期的参数数量if (argc < 2) {printf("Usage 1: %s <gpioNum> \n", argv[0]);printf("Usage 2: %s <gpioNum> <value>\n", argv[0]);printf("Usage 3: %s <gpioNum> <value> <direction>\n", argv[0]);return DAYU800_GPIO_ERR;}// 判断是否有输入参数,如有,则赋值指定gpio口if (argv[1] != NULL) {getValue = atoi(argv[1]);if (getValue >= DAYU800_GPI0_1_3 && getValue <= DAYU800_GPI0_1_6) {gpioNum = getValue;} else {printf("please input the gpioNum between 427 and 430.\n");return DAYU800_GPIO_ERR;}}// 判断gpio口是否已经导出,如未导出则执行对应函数DAYU800_GPIO_IsExport(gpioNum, &getValue);if (getValue == DAYU800_GPIO_NOT_EXPORT) {DAYU800_GPIO_Export(gpioNum, bExport);}if (argc == 2) {// 设置gpio口值取反DAYU800_GPIO_GetValue(gpioNum, &getValue);if(getValue == DAYU800_GPIO_LOW_LEVE){value = DAYU800_GPIO_HIGH_LEVE;}else{value = DAYU800_GPIO_LOW_LEVE;}printf("gpioNum:[%d], curvalue:[%d] setvalue:[%d]\n", gpioNum, getValue,value);}if (argc >=3 && argc <= 4) {if (argv[2] != NULL) {//读取GPIO口设定值getValue = atoi(argv[2]);if (getValue >= DAYU800_GPIO_LOW_LEVE && getValue <= DAYU800_GPIO_HIGH_LEVE) {value = getValue;} else {printf("please input the gpio value 0 (low)or 1 (high).\n");return DAYU800_GPIO_ERR;}}}if (argc == 4) {if (argv[3] != NULL) {//读取GPIO口输入或输出设定getValue = atoi(argv[3]);if (getValue >= DAYU800_GPIO_DIRECTION_IN && getValue <= DAYU800_GPIO_DIRECTION_OUT) {direction = getValue;} else {printf("please input the gpio direction 0 (in)or 1 (out).\n");return DAYU800_GPIO_ERR;}}}// 设置gpio口为输入或输出模式DAYU800_GPIO_SetDirection(gpioNum, direction);// 设置gpio口电平高低DAYU800_GPIO_SetValue(gpioNum, value);// 获取对应gpio口的模式并打印DAYU800_GPIO_GetDirection(gpioNum, &getValue);printf("gpioNum:[%d], direction:[%d]\n", gpioNum, getValue);// 获取对应gpio口的电平值并打印DAYU800_GPIO_GetValue(gpioNum, &getValue);printf("gpioNum:[%d], Value:[%d]\n", gpioNum, getValue);return 0;
}
以上函数实现了是用指令对Dayu800开发板的GPIO引脚的操作,根据输入参数的数量和值,函数执行不同的操作,并在终端打印出相应的信息。支持的指令格式如下:
Usage 1: gpio_dayu800 <gpioNum>
Usage 2: gpio_dayu800 <gpioNum> <value>
Usage 3: gpio_dayu800 <gpioNum> <value> <direction>
3.8.编译代码
#全量编译
./build.sh --product-name dayu800 --gn-args full_mini_debug=false --ccache
编译完成后可以直接烧录out/dayu800/packages/phone/images下生成的档案。#单模块编译
#前提是之前已全量编译过才可以使用单模块编译指令
./build.sh --product-name dayu800 --ccache --build-target product_dayu800
单模块编译后生成bin文件在out/dayu800/product_dayu800/product_dayu800/目录下的gpio_dayu800
3.9.hdc 调试
将 gpio_dayu800推送到开发板,进入hdc工具所在目录,将编译生成的gpio_dayu800拷贝到hdc所在目录,开发板通过Type-C数据线连接到电脑,运行windows自带的“命令提示符”(cmd)窗口
#重新挂载DAYU800开发板的文件系统(以读写权限挂载)
hdc shell mount -o remount,rw / #推送到DAYU800开发板/system/bin/目录
hdc file send gpio_dayu800 /system/bin/
在DAYU800开发板上运行测试程序,使用hdc shell指令进入到开发板终端
hdc shell#接着运行测试指令:
gpio_dayu800 428
gpio_dayu800 429
gpio_dayu800 430
gpio_dayu800 428 0 1 //关灯
gpio_dayu800 428 1 1 //开灯
refer to
- https://blog.csdn.net/lxs_vip/article/details/139391687
- https://wiki.sipeed.com/hardware/zh/lichee/th1520/lpi4a/6_peripheral.html
相关文章:
OpenHarmony-4.基于dayu800 GPIO 实践(2)
基于dayu800 GPIO 进行开发 1.DAYU800开发板硬件接口 LicheePi 4A 板载 2x10pin 插针,其中有 16 个原生 IO,包括 6 个普通 IO,3 对串口,一个 SPI。TH1520 SOC 具有4个GPIO bank,每个bank最大有32个IO: …...
【C++设计模式】观察者模式(1/2):从基础到优化实现
1. 引言 在 C 软件与设计系列课程中,观察者模式是一个重要的设计模式。本系列课程旨在深入探讨该模式的实现与优化。在之前的课程里,我们已对观察者模式有了初步认识,本次将在前两次课程的基础上,进一步深入研究,着重…...
《机器学习数学基础》补充资料:欧几里得空间的推广
在《机器学习数学基础》第 1 章介绍了向量空间,并且说明了机器学习问题通常是在欧几里得空间。然而,随着机器学习技术的发展,特别是 AI 技术开始应用于科学研究中,必然会涉及到其他类型的空间。本文即在《机器学习数学基础》一书所…...
在配置PX4中出现的问题2
想要原教程的请看:第一次配置中出现的问题 前面一切正常(gazebo导入models那一步在刚刚解压好的文件夹里就删不掉stereo_camera等文件,ls打开也看不到,应该时我下的包里面本来就没有),到 make px4_sitl_def…...
2025-2-24-4.9 单调栈与单调队列(基础题)
文章目录 4.9 单调栈与单调队列(基础题)单调栈739. 每日温度42. 接雨水单调队列239. 滑动窗口最大值 4.9 单调栈与单调队列(基础题) 很有趣的两个数据结构。 原视频讲解链接 单调栈 739. 每日温度 题目链接 给定一个整数数组 te…...
python绘图之swarmplot分布散点图
swarmplot 是 Seaborn 提供的一种用于展示分类数据分布的散点图。它的主要作用是将数据点按照分类变量(通常是离散变量)进行分组,并在每个分类中以一种非重叠的方式展示数据点的位置。这种可视化方式可以帮助我们直观地理解数据在不同分类下的…...
数据库之MySQL——事务(一)
1、MySQL之事务的四大特性(ACID)? 原子性(atomicity):一个事务必须视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操…...
Linux学习笔记之文件
1.文件 1.1文件属性 当我们创建文件时,文件就有了对应的属性,可以用mkdir创建目录,touch创建普通文件。用ls -al查看文件属性。 从上图可以看出目录或者文件的所有者,所属组,其他人权限,创建时间等信息。由…...
LLM学习
1、基础概念篇 大模型训练三部曲Pretraining SFT RLHF...
Classic Control Theory | 13 Complex Poles or Zeros (第13课笔记-中文版)
笔记链接:https://m.tb.cn/h.TtdexbP?tkeFAlejKBSzQhttps://m.tb.cn/h.TtdexbP?tkeFAlejKBSzQ...
给小米/红米手机root(工具基本为官方工具)——KernelSU篇
目录 前言准备工作下载刷机包xiaomirom下载刷机包【适用于MIUI和hyperOS】“hyper更新”微信小程序【只适用于hyperOS】 下载KernelSU刷机所需程序和驱动文件 开始刷机设置手机第一种刷机方式【KMI】推荐提取boot或init_boot分区 第二种刷机方式【GKI】不推荐 结语 前言 刷机需…...
【MySQL】表的增删查改(CRUD)(上)
个人主页:♡喜欢做梦 欢迎 👍点赞 ➕关注 ❤️收藏 💬评论 CRUD:Create(新增数据)、Retrieve(查询数据)、Update(修改数据)、Delete(修改数据…...
测试用例的Story是什么?
测试用例的 Story(用户故事)是指描述某个功能或场景的具体用户需求,它通常以简短的业务背景用户操作期望结果的方式呈现,使测试人员能够理解测试的目标和价值。用户故事能够帮助团队更好地设计测试用例,确保功能满足用…...
15.4 FAISS 向量数据库实战:构建毫秒级响应的智能销售问答系统
FAISS 向量数据库实战:构建毫秒级响应的智能销售问答系统 关键词:FAISS 向量数据库、销售知识库构建、相似度检索优化、大规模问答匹配、量化索引技术 1. 销售问答场景的向量化挑战与解决方案 1.1 传统检索方案痛点分析 #mermaid-svg-AeVgih79asJb7lb8 {font-family:"…...
Golang笔记——Interface类型
大家好,这里是,关注 公主号:Goodnote,专栏文章私信限时Free。本文详细介绍Golang的interface数据结构类型,包括基本实现和使用等。 文章目录 Go 语言中的 interface 详解接口定义实现接口空接口 interface{} 示例&…...
如何查看图片的原始格式
问题描述:请求接口的时候,图片base64接口报错,使用图片url请求正常 排查发现是图片格式的问题: 扩展名可能被篡改:如果文件损坏或扩展名被手动修改,实际格式可能与显示的不同,需用专业工具验证…...
FreiHAND (handposeX-json 格式)数据集-release >> DataBall
FreiHAND (handposeX-json 格式)数据集-release 注意: 1)为了方便使用,按照 handposeX json 自定义格式存储 2)使用常见依赖库进行调用,降低数据集使用难度。 3)部分数据集获取请加入:DataBall-X数据球(free) 4)完…...
【Rust中级教程】2.8. API设计原则之灵活性(flexible) Pt.4:显式析构函数的问题及3种解决方案
喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 说句题外话,这篇文章一共5721个字,是我截至目前写的最长的一篇文章&a…...
LabVIEW Browser.vi 库说明
browser.llb 库位于C:\Program Files (x86)\National Instruments\LabVIEW 2019\vi.lib\Platform目录,它是 LabVIEW 平台下用于与网络浏览器相关操作的重要库。该库为 LabVIEW 开发者提供了一系列工具,用于实现网页浏览控制、网页数据获取与交互等功能&a…...
promise的方法有哪些?【JavaScript】
Promise对象在JavaScript中是一种处理异步操作的方式,它提供了一组方法来管理和控制异步操作的结果。以下是一些常用的Promise方法: 以下是对 constructor(executor)、then(onFulfilled, onRejected)、catch(onRejected)、 finally(onFin…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
