RK3568DAYU开发板-平台驱动开发--UART
1、程序介绍
本程序是基于OpenHarmony标准系统编写的平台驱动案例:UART
系统版本:openharmony5.0.0
开发板:dayu200
编译环境:ubuntu22
部署路径: //sample/06_platform_uart
2、基础知识
2.1、UART简介
UART指异步收发传输器(Universal Asynchronous Receiver/Transmitter),是通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输。
两个UART设备的连接示意图如下,UART与其他模块一般用2线(图1)或4线(图2)相连,它们分别是:
- TX:发送数据端,和对端的RX相连。
- RX:接收数据端,和对端的TX相连。
- RTS:发送请求信号,用于指示本设备是否准备好,可接受数据,和对端CTS相连。
- CTS:允许发送信号,用于判断是否可以向对端发送数据,和对端RTS相连。
图1 UART的2线相连
图2 UART的4线相连
UART通信之前,收发双方需要约定好一些参数:波特率、数据格式(起始位、数据位、校验位、停止位)等。通信过程中,UART通过TX发送给对端数据,通过RX接收对端发送的数据。当UART接收缓存达到预定的门限值时,RTS变为不可发送数据,对端的CTS检测到不可发送数据,则停止发送数据。
2.2、UART驱动开发
2.2.1、UART驱动开发接口
为了保证上层在调用UART接口时能够正确的操作UART控制器,核心层在//drivers/hdf_core/framework/support/platform/include/uart/uart_core.h中定义了以下钩子函数,驱动适配者需要在适配层实现这些函数的具体功能,并与钩子函数挂接,从而完成适配层与核心层的交互。
UartHostMethod定义:
struct UartHostMethod {int32_t (*Init)(struct UartHost *host);int32_t (*Deinit)(struct UartHost *host);int32_t (*Read)(struct UartHost *host, uint8_t *data, uint32_t size);int32_t (*Write)(struct UartHost *host, uint8_t *data, uint32_t size);int32_t (*GetBaud)(struct UartHost *host, uint32_t *baudRate);int32_t (*SetBaud)(struct UartHost *host, uint32_t baudRate);int32_t (*GetAttribute)(struct UartHost *host, struct UartAttribute *attribute);int32_t (*SetAttribute)(struct UartHost *host, struct UartAttribute *attribute);int32_t (*SetTransMode)(struct UartHost *host, enum UartTransMode mode);int32_t (*pollEvent)(struct UartHost *host, void *filep, void *table);
};
UartHostMethod结构体成员的回调函数功能说明:
函数 | 入参 | 出参 | 返回值 | 功能 |
---|---|---|---|---|
Init | host:结构体指针,核心层UART控制器 | 无 | HDF_STATUS相关状态 | 初始化Uart设备 |
Deinit | host:结构体指针,核心层UART控制器 | 无 | HDF_STATUS相关状态 | 去初始化Uart设备 |
Read | host:结构体指针,核心层UART控制器 size:uint32_t类型,接收数据大小 | data:uint8_t类型指针,接收的数据 | HDF_STATUS相关状态 | 接收数据RX |
Write | host:结构体指针,核心层UART控制器 data:uint8_t类型指针,传入数据 size:uint32_t类型,发送数据大小 | 无 | HDF_STATUS相关状态 | 发送数据TX |
SetBaud | host:结构体指针,核心层UART控制器 baudRate:uint32_t类型,波特率传入值 | 无 | HDF_STATUS相关状态 | 设置波特率 |
GetBaud | host:结构体指针,核心层UART控制器 | baudRate:uint32_t类型指针,传出的波特率 | HDF_STATUS相关状态 | 获取当前设置的波特率 |
GetAttribute | host:结构体指针,核心层UART控制器 | attribute:结构体指针,传出的属性值(见uart_if.h中UartAttribute定义) | HDF_STATUS相关状态 | 获取设备uart相关属性 |
SetAttribute | host:结构体指针,核心层UART控制器 attribute:结构体指针,属性传入值 | 无 | HDF_STATUS相关状态 | 设置设备UART相关属性 |
SetTransMode | host:结构体指针,核心层UART控制器 mode:枚举值(见uart_if.h中UartTransMode定义),传输模式 | 无 | HDF_STATUS相关状态 | 设置传输模式 |
PollEvent | host:结构体指针,核心层UART控制器 filep:void类型指针file table:void类型指针table | 无 | HDF_STATUS相关状态 | poll轮询机制 |
2.2.2、UART驱动开发步骤
UART模块适配HDF框架包含以下四个步骤:
- 实例化驱动入口。
- 配置属性文件。
- 实例化UART控制器对象。
- 驱动调试。
2.3、UART应用开发
UART模块应用比较广泛,主要用于实现设备之间的低速串行通信,例如输出打印信息,当然也可以外接各种模块,如GPS、蓝牙等。
2.3.1、接口说明
UART模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/uart_if.h。
UART驱动API接口功能介绍如下所示:
接口名 | 接口描述 |
---|---|
DevHandle UartOpen(uint32_t port) | UART获取设备句柄 |
void UartClose(DevHandle handle) | UART释放设备句柄 |
int32_t UartRead(DevHandle handle, uint8_t *data, uint32_t size) | 从UART设备中读取指定长度的数据 |
int32_t UartWrite(DevHandle handle, uint8_t *data, uint32_t size) | 向UART设备中写入指定长度的数据 |
int32_t UartGetBaud(DevHandle handle, uint32_t *baudRate) | UART获取波特率 |
int32_t UartSetBaud(DevHandle handle, uint32_t baudRate) | UART设置波特率 |
int32_t UartGetAttribute(DevHandle handle, struct UartAttribute *attribute) | UART获取设备属性 |
int32_t UartSetAttribute(DevHandle handle, struct UartAttribute *attribute) | UART设置设备属性 |
int32_t UartSetTransMode(DevHandle handle, enum UartTransMode mode) | UART设置传输模式 |
(1)UartOpen
在使用UART进行通信时,首先要调用UartOpen获取UART设备句柄,该函数会返回指定端口号的UART设备句柄。
DevHandle UartOpen(uint32_t port);
UartOpen参数定义如下:
参数 | 参数描述 |
---|---|
port | UART设备号 |
UartOpen返回值定义如下:
返回值 | 返回值描述 |
---|---|
NULL | 获取UART设备句柄失败 |
设备句柄 | UART设备句柄 |
假设系统中的UART端口号为1,获取该UART设备句柄的示例如下:
DevHandle handle = NULL; // UART设备句柄
uint32_t port = 1; // UART设备端口号handle = UartOpen(port);
if (handle == NULL) {HDF_LOGE("UartOpen: open uart_%u failed!\n", port);return;
}
(2)UartSetBaud
在通信之前,需要设置UART的波特率。
int32_t UartSetBaud(DevHandle handle, uint32_t baudRate);
UartSetBaud参数定义如下:
参数 | 参数描述 |
---|---|
handle | UART设备句柄 |
baudRate | 待设置的波特率值 |
UartSetBaud返回值定义如下:
返回值 | 返回值描述 |
---|---|
HDF_SUCCESS | UART设置波特率成功 |
负数 | UART设置波特率失败 |
(3)UartGetBaud
设置UART的波特率后,可以通过获取波特率接口来查看UART当前的波特率。
int32_t UartGetBaud(DevHandle handle, uint32_t *baudRate);
UartGetBaud参数定义如下:
参数 | 参数描述 |
---|---|
handle | UART设备句柄 |
baudRate | 接收波特率值的指针 |
UartGetBaud返回值定义如下:
返回值 | 返回值描述 |
---|---|
HDF_SUCCESS | UART获取波特率成功 |
负数 | UART获取波特率失败 |
(4)UartSetAttribute
在通信之前,需要设置UART的设备属性。
int32_t UartSetAttribute(DevHandle handle, struct UartAttribute *attribute);
UartSetAttribute参数定义如下:
参数 | 参数描述 |
---|---|
handle | UART设备句柄 |
attribute | 待设置的设备属性 |
UartSetAttribute返回值定义如下:
返回值 | 返回值描述 |
---|---|
HDF_SUCCESS | UART设置设备属性成功 |
负数 | UART设置设备属性失败 |
(5)UartGetAttribute
设置UART的设备属性后,可以通过获取设备属性接口来查看UART当前的设备属性。
int32_t UartGetAttribute(DevHandle handle, struct UartAttribute *attribute);
UartGetAttribute参数定义如下:
参数 | 参数描述 |
---|---|
handle | UART设备句柄 |
attribute | 接收UART设备属性的指针 |
UartGetAttribute返回值定义如下:
返回值 | 返回值描述 |
---|---|
HDF_SUCCESS | UART获取设备属性成功 |
负数 | UART获取设备属性失败 |
(6)UartSetTransMode
在通信之前,需要设置UART的传输模式。
int32_t UartSetTransMode(DevHandle handle, enum UartTransMode mode);
UartSetTransMode参数定义如下:
参数 | 参数描述 |
---|---|
handle | UART设备句柄 |
mode | 待设置的传输模式 |
UartSetTransMode返回值定义如下:
返回值 | 返回值描述 |
---|---|
HDF_SUCCESS | UART设置传输模式成功 |
负数 | UART设置传输模式失败 |
(7)UartWrite
向UART设备写入指定长度的数据。
int32_t UartWrite(DevHandle handle, uint8_t *data, uint32_t size);
UartWrite参数定义如下:
参数 | 参数描述 |
---|---|
handle | UART设备句柄 |
data | 待写入数据的指针 |
size | 待写入数据的长度 |
UartWrite返回值定义如下:
返回值 | 返回值描述 |
---|---|
HDF_SUCCESS | UART写数据成功 |
负数 | UART写数据失败 |
(8)UartRead
从UART设备中读取指定长度的数据。
int32_t UartRead(DevHandle handle, uint8_t *data, uint32_t size);
UartRead参数定义如下:
参数 | 参数描述 |
---|---|
handle | UART设备句柄 |
data | 接收读取数据的指针 |
size | 待读取数据的长度 |
UartRead返回值定义如下:
返回值 | 返回值描述 |
---|---|
非负数 | UART读取到的数据长度 |
负数 | UART读取数据失败 |
(9)UartClose
UART通信完成之后,需要销毁UART设备句柄。
void UartClose(DevHandle handle);
UartClose参数定义如下:
参数 | 参数描述 |
---|---|
handle | UART设备句柄 |
2.2.2、开发流程
使用UART的一般流程如下图所示:
3、程序解析
3.1、代码目录
3.2、接口流程梳理
- UartOpen执行流程
//drivers\hdf_core\framework\support\platform\src\uart\uart_if.c
DevHandle UartOpen(uint32_t port)
|-->handle = UartGetObjGetByBusNum(port)|-->ret = snprintf_s(name, UART_HOST_NAME_LEN + 1, UART_HOST_NAME_LEN, "HDF_PLATFORM_UART_%u", num);|-->return (void *)DevSvcManagerClntGetService(name);//获取设备服务管理实例,属于hdf驱动框架的部分,drivers\hdf_core\framework\core\host\src\devsvc_manager_clnt.c
|-->ret = UartHostRequest((struct UartHost *)handle)|-->ret = host->method->Init(host);
|-->(DevHandle)handle;
- UartHostRead执行流程
//drivers\hdf_core\framework\support\platform\include\uart\uart_core.h
static inline int32_t UartHostRead(struct UartHost *host, uint8_t *data, uint32_t size)
|-->return host->method->Read(host, data, size);
由上可见设备节点由全局变量g_i2cManager提供,此变量由下文中平台驱动linux_uart_adapter(drivers\hdf_core\adapter\khdf\linux\platform\uart\uart_adapter.c)进行设置。
3.3、OpenHarmony UART平台驱动
3.3.1、驱动实例化驱动入口
驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
drivers/hdf_core/adapter/khdf/linux/platform/uart/uart_adapter.c
struct HdfDriverEntry g_hdfUartchdog = {.moduleVersion = 1,.moduleName = "HDF_PLATFORM_UART",.Bind = HdfUartBind,.Init = HdfUartInit,.Release = HdfUartRelease,
};
HDF_INIT(g_hdfUartchdog);
3.3.2、配置属性文件
完成驱动入口注册之后**,需要在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关**。在//vendor/hihope/rk3568/hdf_config/khdf/device_info/device_info.hcs中可以看到如下信息:
device_uart :: device {device0 :: deviceNode {policy = 1; // 驱动服务发布的策略,policy大于等于1(用户态可见为2,仅内核态可见为1)priority = 40; // 驱动启动优先级permission = 0644; // 驱动创建设备节点权限moduleName = "HDF_PLATFORM_UART"; // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致serviceName = "HDF_PLATFORM_UART_0"; // 驱动对外发布服务的名称,必须唯一,必须要按照HDF_PLATFORM_UART_X的格式,X为UART控制器编号deviceMatchAttr = "rockchip_rk3568_uart_0"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值一致}device1 :: deviceNode {policy = 2;permission = 0644;priority = 40;moduleName = "HDF_PLATFORM_UART";serviceName = "HDF_PLATFORM_UART_1";deviceMatchAttr = "rockchip_rk3568_uart_1";}
}
- device2是我们新增的设备节点,给uart5使用。
- policy必须为2,表示对内核态和用户态提供服务。否则,应用程序无法调用。
- HDF_PLATFORM_UART_2,后面跟着的数据“2”,是UartOpen()的端口号。
- HDF_PLATFORM_UART_2,后面跟着的数据“2”,必须是递增的。
如有多个器件信息,则需要在device_info.hcs文件增加对应的deviceNode信息。器件属性值与核心层UartDev成员的默认值或限制范围有密切关系,比如Uart设备号,需要在uart_config.hcs文件中增加对应的器件属性
uart_config.hcs 配置参考//vendor/hihope/rk3568/hdf_config/khdf/platform/rk3568_uart_config.hcs,具体修改如下:
root {platform {uart_config {template uart_device {serviceName = "";match_attr = "";driver_name = "ttyS";num = 0;}device_uart_0x0000 :: uart_device {match_attr = "rockchip_rk3568_uart_0";}device_uart_0x0001 :: uart_device {num = 1; // 序号,是Linux的/dev/ttySXXX,XXX即是nummatch_attr = "rockchip_rk3568_uart_1"; // 必须和device_info.hcs中对应的设备的deviceMatchAttr值一致// 注意rockchip_rk3568_uart_XX,XX表示的OH的UART序号,必须从0开始,顺序递增}}}
}
- device_uart_0x0002是新增的,为uart5准备的。
- match_attr的名称必须是rockchip_rk3568_uart_2。
- UartOpen函数参数port,则表示上述uart设备排列序号。比如:uart num = 5的UartOpen函数port = 2。
3.3.3、实例化UART控制器对象
完成驱动入口注册之后,下一步就是以核心层UartDev对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化UartDev成员UartHostMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
// 定义UartHostMethod成员g_uartHostMethod,实现uart相应接口
static struct UartHostMethod g_uartHostMethod = {.Init = UartAdapterInit,.Deinit = UartAdapterDeInit,.Read = UartAdapterRead,.Write = UartAdapterWrite,.SetBaud = UartAdapterSetBaud,.GetBaud = UartAdapterGetBaud,.SetAttribute = UartAdapterSetAttribute,.GetAttribute = UartAdapterGetAttribute,.SetTransMode = UartAdapterSetTransMode,
};static int32_t HdfUartBind(struct HdfDeviceObject *obj)|-->return (UartHostCreate(obj) == NULL) ? HDF_FAILURE : HDF_SUCCESS;//为uarthost分配内存并与device进行绑定,drivers\hdf_core\framework\support\platform\src\uart\uart_core.c|-->host = (struct UartHost *)OsalMemCalloc(sizeof(*host));|-->host->device = device;|-->device->service = &(host->service);//在上文提到的函数(DevSvcManagerClntGetService)返回,此为uarthost类的第一个成员函数,可强制类型指针转换。
static int32_t HdfUartInit(struct HdfDeviceObject *obj);|-->host = UartHostFromDevice(obj);//获取host->service,作为指针头|-->iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE)//获取设备信息的单实例|-->if (iface->GetUint32(obj->property, "num", &host->num, 0) != HDF_SUCCESS)//解析设备树配置文件
static void HdfUartRelease(struct HdfDeviceObject *obj);
struct HdfDriverEntry g_hdfUartchdog = {.moduleVersion = 1,.moduleName = "HDF_PLATFORM_UART",.Bind = HdfUartBind,.Init = HdfUartInit,.Release = HdfUartRelease,
};
HDF_INIT(g_hdfUartchdog);
3.5、应用程序
3.5.1、uart_test.c
uart相关头文件如下所示:
#include "uart_if.h" // UART标准接口头文件
主函数定义UART接口调用,具体如下:
int main(int argc, char* argv[])
{DevHandle handle = NULL;struct UartAttribute attribute;int32_t ret = 0;uint8_t wbuff[STRING_MAXSIZE] = "HelloWorld";uint8_t rbuff[STRING_MAXSIZE] = { 0 };......attribute.dataBits = UART_ATTR_DATABIT_8; // UART传输数据位宽,一次传输7个bitattribute.parity = UART_ATTR_PARITY_NONE; // UART传输数据无校检attribute.stopBits = UART_ATTR_STOPBIT_1; // UART传输数据停止位为1位attribute.rts = UART_ATTR_RTS_DIS; // UART禁用RTSattribute.cts = UART_ATTR_CTS_DIS; // UART禁用CTSattribute.fifoRxEn = UART_ATTR_RX_FIFO_EN; // UART使能RX FIFOattribute.fifoTxEn = UART_ATTR_TX_FIFO_EN; // UART使能TX FIFOhandle = UartOpen(m_uart_port);if (handle == NULL) {PRINT_ERROR("UartOpen: open uart port %u failed!\n", m_uart_port);return -1;}PRINT_INFO("UartOpen successful and uart port = %d\n", m_uart_port);// 设置UART波特率ret = UartSetBaud(handle, m_uart_baudrate);if (ret != HDF_SUCCESS) {PRINT_ERROR("UartSetBaud: set baud failed, ret %d\n", ret);goto ERR;}PRINT_INFO("UartSetBaud successful and uart baudrate = %d\n", m_uart_baudrate);// 设置UART设备属性ret = UartSetAttribute(handle, &attribute);if (ret != HDF_SUCCESS) {PRINT_ERROR("UartSetAttribute: set attribute failed, ret %d\n", ret);goto ERR;}PRINT_INFO("UartSetAttribute successful\n");// 获取UART设备属性ret = UartGetAttribute(handle, &attribute);if (ret != HDF_SUCCESS) {PRINT_ERROR("UartGetAttribute: get attribute failed, ret %d\n", ret);goto ERR;}PRINT_INFO("UartGetAttribute successful\n");// 设置UART传输模式为非阻塞模式ret = UartSetTransMode(handle, UART_MODE_RD_NONBLOCK);if (ret != HDF_SUCCESS) {PRINT_ERROR("UartSetTransMode: set trans mode failed, ret %d\n", ret);goto ERR;}PRINT_INFO("UartSetTransMode successful\n");// 向UART设备写入数据ret = UartWrite(handle, wbuff, (uint32_t)strlen((char *)wbuff));if (ret != HDF_SUCCESS) {PRINT_ERROR("UartWrite: write data failed, ret %d\n", ret);goto ERR;}PRINT_INFO("UartWrite successful and wbuff = %s\n", wbuff);// 从UART设备读取5字节的数据ret = UartRead(handle, rbuff, STRING_MAXSIZE);if (ret < 0) {PRINT_ERROR("UartRead: read data failed, ret %d\n", ret);goto ERR;}PRINT_INFO("UartRead successful and rbuff = %s\n", rbuff);ERR:// 销毁UART设备句柄UartClose(handle);return ret;
}
3.5.2、BUILD.gn
编写应用程序的BUILD.gn,具体内容如下:
import("//build/ohos.gni")
import("//drivers/hdf_core/adapter/uhdf2/uhdf.gni")print("samples: compile rk3568_uart_test")
ohos_executable("rk3568_uart_test") {sources = [ "uart_test.c" ]include_dirs = ["$hdf_framework_path/include","$hdf_framework_path/include/core","$hdf_framework_path/include/osal","$hdf_framework_path/include/platform","$hdf_framework_path/include/utils","$hdf_uhdf_path/osal/include","$hdf_uhdf_path/ipc/include","//base/hiviewdfx/hilog/interfaces/native/kits/include","//third_party/bounds_checking_function/include",]deps = ["$hdf_uhdf_path/platform:libhdf_platform","$hdf_uhdf_path/utils:libhdf_utils","//base/hiviewdfx/hilog/interfaces/native/innerkits:libhilog",]cflags = ["-Wall","-Wextra","-Werror","-Wno-format","-Wno-format-extra-args",]part_name = "rk3568_uart_test"install_enable = true
}
3.4.3、bundle.json
编写应用程序的bundle.json,具体内容如下:
{"name": "@ohos/uart_test","description": "rk3568_uart_test example.","version": "3.1","license": "Apache License 2.0","publishAs": "code-segment","segment": {"destPath": "sample/06_platform_uart"},"dirs": {},"scripts": {},"component": {"name": "rk3568_uart_test","subsystem": "sample","syscap": [],"features": [],"adapted_system_type": ["mini","small","standard"],"rom": "10KB","ram": "10KB","deps": {"components": ["hdf_core","hilog"],"third_party": []},"build": {"sub_component": ["//sample/06_platform_uart:rk3568_uart_test"],"inner_kits": [],"test": []}}
}
4、程序编译
sudo ./build.sh --product-name rk3568 --build-target rk3568_uart_test
5、运行结果
运行如下:
注意:
- rbuff获取的时候可能为空。因为本次案例是基于非阻塞,电脑端发送的串口可能没有获取到数据。
建议:
- 读者可以尝试使用堵塞方式,再测试看看。
6、参考资料
- UART平台驱动开发
- UART应用程序开发
- OpenHarmony平台驱动案例–UART
相关文章:

RK3568DAYU开发板-平台驱动开发--UART
1、程序介绍 本程序是基于OpenHarmony标准系统编写的平台驱动案例:UART 系统版本:openharmony5.0.0 开发板:dayu200 编译环境:ubuntu22 部署路径: //sample/06_platform_uart 2、基础知识 2.1、UART简介 UART指异步收发传输器(Univer…...
ubuntu 安装 Redis 5.0.8 的完整步骤
以下是根据前面的沟通记录整理的完整安装过程和依赖项,确保在 Ubuntu 22 上成功安装 Redis 5.0.8。 安装 Redis 5.0.8 的完整步骤 1. 安装依赖 在编译和运行 Redis 之前,需要安装一些必要的工具和库: sudo apt update sudo apt install bu…...

制造企业搭建AI智能生产线怎么部署?
制造商需要精准协调生产和发货,确保订单及时交付。MES、ERP、CRM 系统与生产线集成,对生产管理流程、物料跟踪、品控、确定货期至关重要。如果某个系统发生延迟或者效率低下,会在造成整个生产环节停滞,影响最终交付,导…...

深度学习驱动的超高清图修复技术——综述
Deep Learning-Driven Ultra-High-Definition Image Restoration: A Survey Liyan Wang, Weixiang Zhou, Cong Wang, Kin-Man Lam, Zhixun Su, Jinshan Pan Abstract Ultra-high-definition (UHD) image restoration aims to specifically solve the problem of quali…...
unix/linux source 命令,其内部结构机制
要理解 source (或 .) 命令的内部结构机制,我们需要戴上“操作系统”和“解释器设计”的眼镜,深入到 Shell 如何管理其状态以及如何执行命令的层面。 虽然我们无法直接看到 Shell 内部的 C 代码(除非我们去阅读 Bash 或 Zsh 的源码),但我们可以基于其行为和操作系统的原理…...
【LLM】FastAPI入门教程
note FastAPI 是一个现代的、快速(高性能)的 Web 框架,用于构建 API(应用程序编程接口)。它基于 Python 3.7,使用了 Python 类型提示(type hints),并且具有自动化的文档…...
进程同步机制-信号量机制-记录型信号量机制中的的wait和signal操作
wait和signal是记录型信号量机制中用于实现进程同步与互斥的两个重要操作, wait 操作 wait(semaphores *S) {S->value --;if (S->value<0) block(S->list) }请求资源:S->value --; 这一步表示进程请求一个单位的资源,将信号…...
gitlib 常见命令
git clone <项目URL> # 从 GitLab 拉取代码到本地 git status 查看状态 git diff 文件路径 查看修改位置 git diff 文件路径 查看修改位置 black -l 180 路径 格式化文件 git add 路径 (可以多个) 添加修改到暂存区 git commit -m “提交说明…...

Azure DevOps 管道部署系列之二IIS
本博客旨在提供如何使用 Azure DevOps YAML 管道部署到虚拟机上的 IIS 的实用指南。 开始之前,您需要做好以下准备: 您拥有要部署的服务器的访问权限以及 PowerShell 的管理员访问权限。您拥有要部署的远程服务器的互联网访问权限。您拥有在服务器上安装 .NET Core 托管包的…...

Vue.js教学第十七章:Vue 与后端交互(一),Axios 基础
Vue 与后端交互(一):Axios 基础 在现代前端开发中,Vue 应用通常需要与后端 API 进行数据交互,以实现动态数据的获取和提交。Axios 是一个基于 Promise 的 HTTP 客户端,广泛用于 Vue 项目中与后端进行通信。本文将深入讲解 Axios 的基本用法,包括如何通过 Axios 发送 GE…...

人工智能浪潮下,制造企业如何借力DeepSeek实现数字化转型?
一、DeepSeek技术概述 DeepSeek,凭借其强大的深度学习和自然语言处理能力,能够理解复杂问题并提供精准解决方案。它不仅能够作为学习、工作、生活的助手,满足用户在不同场景下的需求,更能在制造业中发挥重要作用。通过自然语言交…...
NodeJS全栈开发面试题讲解——P2Express / Nest 后端开发
✅ 2.1 Express 的中间件机制?如何组织一个 RESTful API 项目? 面试官好,我来讲讲 Express 的中间件机制,它是 Express 架构的核心,也是组织 RESTful 项目的基础。 🧩 什么是中间件? 中间件&am…...

从线性代数到线性回归——机器学习视角
真正不懂数学就能理解机器学习其实是个神话。我认为,AI 在商业世界可以不懂数学甚至不懂编程也能应用,但对于技术人员来说,一些基础数学是必须的。本文收集了我认为理解学习本质所必需的数学基础,至少在概念层面要掌握。毕竟&…...

计算机网络相关发展以及常见性能指标
目录 一、因特网概述 1.1 基本概念 1.2 因特网发展的三个阶段 1.3 英特网服务提供者ISP 1.4 英特网的标准化工作 1.5 因特网的组成 1.6 简单总结 二、3种交换方式 2.1 电路交换(Circuit Switching) 2.2 分组交换(Packet Switching&…...

通义灵码:基于MCP的火车票小助手系统全流程设计与技术总结
具体操作步骤请访问:https://blog.csdn.net/ailuloo/article/details/148319336?spm1001.2014.3001.5502 前沿技术应用全景图 一、项目背景与需求分析 目标:基于12306 MCP接口,开发一款解决高峰出行(春运/节假日)痛…...

为什么建立 TCP 连接时,初始序列号不固定?
主要原因有两个方面: 很大程度上避免历史报文被下一个相同四元组的 TCP 连接接收问题(主要方面)防止黑客伪造相同序列号的 TCP 报文被接收 接下来,详细说说第一点 假设每次建立 TCP 连接时,客户端和服务端的初始序列…...

VBA数据库解决方案二十:Select表达式From区域Where条件Order by
《VBA数据库解决方案》教程(版权10090845)是我推出的第二套教程,目前已经是第二版修订了。这套教程定位于中级,是学完字典后的另一个专题讲解。数据库是数据处理的利器,教程中详细介绍了利用ADO连接ACCDB和EXCEL的方法…...

NX753NX756美光科技闪存NX784NX785
技术解读与产品特性 美光科技的NX系列闪存,包括NX753、NX756、NX784、NX785等型号,代表了当前存储技术的前沿水平。这些产品基于先进的NAND闪存技术,采用业界领先的3D TLC NAND技术,实现了高速的数据读写能力。3D TLC NAND技术通…...

使用 pytesseract 构建一个简单 OCR demo
简介 pytesseract 库是 Google Tesseract OCR (光学字符识别)引擎的一个 Python 封装库,使用广泛且功能强大。 构建 使用 pytesseract 构建一个简单 OCR demo。 步骤一:安装必要的库 您需要在您的 Python 环境中安装 pytessera…...
Cesium快速入门到精通系列教程三:添加物体与3D建筑物
Cesium中添加物体与3D建筑物,对于大规模城市模型,推荐使用 3D Tileset;对于简单几何图形,可以使用 Entity API;对于复杂模型,可以使用 GLTF 格式: 一、添加一个点: 在 Cesium 1.93…...

git 如何解决分支合并冲突(VS code可视化解决+gitLab网页解决)
1、定义:两个分支修改了同一文件的同一行代码,无法自动决定如何合并代码,需要人工干预的情况。(假设A提交了文件a,此时B在未拉取代码的情况下,直接提交是会报错的,此时需要拉取之后再提交才会成功ÿ…...

【CF】Day72——Codeforces Round 890 (Div. 2) CDE1 (二分答案 | 交互 + 分治 | ⭐树上背包)
C. To Become Max 题目: 思路: 二分挺好想的,但是check有点不好写 看到最大值,试试二分,如果 x 可以,那么 x - 1 肯定也可以,所以具有单调性,考虑二分 如何check呢?由于…...

单片机寄存器的四种主要类型!
1. 控制寄存器(Control Registers) 专业定义:用于配置硬件行为或触发操作的寄存器。 大白话: 相当于设备的“控制面板”,通过写入特定值来开关功能或调整参数。例如&am…...

智能嗅探AJAX触发:机器学习在动态渲染中的创新应用
一、问题描述:数据加载变“隐形”,采集举步维艰 随着Web技术不断发展,越来越多网站采用了AJAX、动态渲染等技术来加载数据。以今日头条(https://www.toutiao.com)为例,用户打开网页时并不会一次性加载所有…...

【计算机网络】Linux下简单的UDP服务器(超详细)
套接字接口 我们把服务器封装成一个类,当我们定义出一个服务器对象后需要马上初始化服务器,而初始化服务器需要做的第一件事就是创建套接字。 🌎socket函数 这是Linux中创建套接字的系统调用,函数原型如下: int socket(int domain, int typ…...
Java并发编程实战 Day 3:volatile关键字与内存可见性
【Java并发编程实战 Day 3】volatile关键字与内存可见性 开篇 欢迎来到《Java并发编程实战》系列的第3天!本系列旨在带领你从基础到高级逐步掌握Java并发编程的核心概念和最佳实践。 今天我们将重点探讨volatile关键字及其在多线程程序中确保内存可见性的作用。我…...

华为OD机试真题——报文回路(2025A卷:100分)Java/python/JavaScript/C/C++/GO最佳实现
2025 A卷 100分 题型 本专栏内全部题目均提供Java、python、JavaScript、C、C++、GO六种语言的最佳实现方式; 并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析; 本文收录于专栏:《2025华为OD真题目录+全流程解析+备考攻略+经验分…...
K8s工作流程与YAML实用指南
K8s 工作流程 K8s 采用声明式管理(用户说"要什么",K8s 负责"怎么做")方式,通过 YAML 文件描述期望的状态,K8s控制平面会自动确保实际状态与期望状态一致。 核心工作流程如下: 用户提交…...

功能丰富的PDF处理免费软件推荐
软件介绍 今天给大家介绍一款超棒的PDF工具箱,它处理PDF文档的能力超强,而且是完全免费使用的,没有任何限制。 TinyTools(PC)这款软件,下载完成后即可直接打开使用。在使用过程中,操作完毕后&a…...

Java补充(Java8新特性)(和IO都很重要)
一、Lambda表达式 1.1、为什么使用Lambda表达式 Lambda表达式起步案例 下面源码注释是传统写法,代码是简写表达式写法 import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.function.Consumer;/* * 学什么…...