GD32F407单片机开发入门(二十八)USB口介绍及CDC类虚拟串口通讯详解及源码

文章目录

    • 一.概要
    • 二.USB2.0基本介绍及虚拟串口介绍
    • 三.GD32单片机USB模块框图
    • 四.GD32单片机USB设备模式
    • 五.GD32F407VET6 USB设备CDC类
    • 六.配置一个USB虚拟串口收发例程
    • 七.工程源代码下载
    • 八.小结

一.概要

GD32F407VET6USB虚拟串口是一种采用GD32F407VET6单片机,通过USB接口连接电脑,将电脑的USB接口转换成串口接口,实现与电脑的通信的一种转换器。它可以实现与电脑的通信,还可以实现与外部设备的通信,广泛应用于工业控制、智能家居、智能硬件等领域。

本文介绍了GD32单片机USB口的基本概念,内部结构,以及用USB虚拟串口进行数据通讯的例程。

二.USB2.0基本介绍及虚拟串口介绍

USB2.0使用一对差分信号传输数据,并可以为USB设备提供电源。差分信号名称一般标示为“D+”和“D-”。
USB2.0可以支持三种传输速率:低速USB设备传输速率为1.5Mbps,全速USB设备传输速率为12Mbps,高速USB设备传输速率为480Mbps。GD32F407VET6支持全速USB设备传输,最快12Mbps。
在硬件电路方面,全速USB设备内部的“D+”信号应该通过1.5K的电阻上拉到3~3.6V,单片机USB口原理图如下所示,PA11,PA12连接单片机对应的引脚。
在这里插入图片描述

USB虚拟串口,简称VCP,是Virtual COM Port的简写,它是利用 USB的 CDC类来实现的一种通信接口。我们可以利用GD32自带的USB功能,来实现一个USB虚拟串口,从而通过USB,实现电脑与GD32单片机的数据互传。

三.GD32单片机USB模块框图

USB全速(USBFS)控制器为便携式设备提供了一套USB通信解决方案。USBFS不仅提供了主机模式和设备模式,也提供了遵循HNP(主机协商协议)和SRP(会话请求协议)的OTG模式。USBFS包含了一个内部的全速USB PHY,并且不再需要外部PHY芯片。USBFS可提供USB 2.0协议所定义的所有四种传输方式(控制传输、批量传输、中断传输和同步传输)。

在这里插入图片描述

SIE
硬件识别同步信号、进行比特填充、产生以及校验CRC、产生以及验证PID、握手 。根据外设事件来产生SOF、复位信号等。

USB FS PHY
内部PHY支持主机模式下的全速和低速、设备模式下全速以及具备HNP和SRP的OTG协议。USBFS所使用的USB时钟需要配置为48MHz。该48MHz USB时钟从系统内部时钟产生,并且其时钟源和分频器需要在RCU模块中配置。

OTG Control
OTG Control模块主要用于管理其集成的USB On-The-Go(OTG)功能,实现设备在主机(Host)和从设备(Device)模式间的动态切换及控制。主要功能有模式切换(主机/设备)、电源管理(比如VBUS供电的控制)、会话请求协议(Session Request Protocol, SRP)和主机协商协议(Host Negotiation Protocol, HNP),以及相关的寄存器配置。

Host Port Control
Host Port Control模块专门用于管理和控制其USB OTG(On-The-Go)功能中的主机模式(Host Mode)操作。当单片机作为USB主机时,该模块负责与连接的USB从设备(如U盘、键盘、鼠标等)进行通信、供电及数据传输的底层控制。

根据USB标准定义,USB全速模块采用了固定的48MHz时钟。要使用USBD,需要打开两个时钟,一个是USB控制器时钟,它的频率必须配到48MHz,另一个是APB1到USB接口时钟,它也是APB1的总线时钟,其频率可以高于也可以低于48MHz。

四.GD32单片机USB设备模式

USB一般有两种模式,主机模式,设备模式。

USB主机模式:‌在主机模式下,‌单片机能够枚举外部USB设备,‌如键盘、‌鼠标、‌闪存盘等,‌并对其进行配置和管理。‌这种模式适用于需要同时连接多个外部设备并进行数据交换的复杂应用场景。

USB设备模式:‌在设备模式下,‌单片机作为USB设备的角色,‌可以与主机进行通信。‌这包括配置USB设备描述符、‌初始化USB控制器、‌编写类处理函数等,‌以实现特定的通信需求。‌GD32支持多种USB类,‌如CDC(‌通信设备类)‌、‌HID(‌人机接口设备类)‌、‌MSC(‌大容量存储类)‌等,‌以满足不同的应用场景。

GD32F407VET6支持主机模式也支持设备模式,在设备模式下,GD32可以模拟各种USB类设备,如键盘、鼠标、存储设备等,开发者需要配置USB接口并实现特定的USB类。

在这里插入图片描述

五.GD32F407VET6 USB设备CDC类

CDC(Communication Device Class)类是 USB2.0 标准下的一个子类,定义了通信相关设备的抽象集合,我们虚拟串口通信就是CDC类。USB2.0标准下定义了很多子类,有音频类,CDC类,HID,打印,大容量存储类HUB,智能卡等等,这些在urb.org 官网上有具体的定义,这里我们主要讲的是通信类CDC。

USB CDC类的通信部分主要包含三部分:枚举过程、虚拟串口操作和数据通信。其中虚拟串口操作部分并不一定强制需要,因为若跳过这些虚拟串口的操作,实际上USB依然是可以通信的,之所以会有虚拟串口操作,主要是我们通常使用PC作为Host端,在PC端使用一个串口工具来与其进行通信,PC端的对应驱动将其虚拟成一个普通串口,这样一来,可以方便PC端软件通过操作串口的方式来与其进行通信,但实际上,Host端与Device端物理上是通过USB总线来进行通信的,与串口没有关系,这一虚拟化过程,起决定性作用的是对应驱动,包含如何将每一条具体的虚拟串口操作对应到实际上的USB操作。这里需要注意地是,Host端与Device端的USB通信速率并不受所谓的串口波特率影响,它就是标准的USB2.0全速(12Mbps)速度,实际速率取决于总线的实际使用率、驱动访问USB外设有效速率(两边)以及外部环境对通信本身造成的干扰率等等因素组成。
USB CDC(Communication Device Class)类的枚举是USB设备插入主机时,主机识别其为通信设备(如虚拟串口)并完成配置的关键过程。其核心在于描述符的声明和接口的划分,确保主机能够正确加载驱动并建立通信通道。
CDC枚举的主要流程
1.设备插入与复位
2.设备描述符(Device Descriptor)
3.配置描述符(Configuration Descriptor)
4.CDC类特定描述符(Class-Specific Descriptors)
5.端点描述符(Endpoint Descriptor)
6.主机响应流程

CDC软件框架简介
当USBD设备初始化且枚举完成后,USB设备首先通过cdc_acm_check_ready()函数check是否准备数据发送,如果不需要发送就调用cdc_acm_data_receive()函数接收上位机发送的数据,如果需要发送就调用cdc_acm_data_send()将接收到的数据发送给主机。

设备描述符如下所示,其中bDeviceClass 为0x02,表明当前设备为CDC设备类。

__ALIGN_BEGIN const usb_desc_dev cdc_dev_desc __ALIGN_END =
{.header = {.bLength          = USB_DEV_DESC_LEN, .bDescriptorType  = USB_DESCTYPE_DEV,},.bcdUSB                = 0x0200U,.bDeviceClass          = USB_CLASS_CDC,.bDeviceSubClass       = 0x00U,.bDeviceProtocol       = 0x00U,.bMaxPacketSize0       = USB_FS_EP0_MAX_LEN,.idVendor              = USBD_VID,.idProduct             = USBD_PID,.bcdDevice             = 0x0100U,.iManufacturer         = STR_IDX_MFC,.iProduct              = STR_IDX_PRODUCT,.iSerialNumber         = STR_IDX_SERIAL,.bNumberConfigurations = USBD_CFG_MAX_NUM,
};

由配置描述符可知,该USB虚拟串口设备包含两个接口:CMD命令接口和data数据接口。CMD命令接口包含一个IN端点,用于传输命令,该端点采用中断传输方式,轮询间隔为5ms,最大包长为8字节。data数据接口包含一个OUT端点和一个IN端点,这两个端点均采用批量传输方式,最大包长为USB_CDC_DATA_PACKET_SIZE(64)字节。另外,该配置描述符中包含了一些类特殊接口描述符,具体请读者参阅CDC类标准协议。

/* USB device configuration descriptor */
__ALIGN_BEGIN const usb_cdc_desc_config_set cdc_config_desc __ALIGN_END = 
{.config = {.header = {.bLength         = sizeof(usb_desc_config), .bDescriptorType = USB_DESCTYPE_CONFIG,},.wTotalLength         = USB_CDC_ACM_CONFIG_DESC_SIZE,.bNumInterfaces       = 0x02U,.bConfigurationValue  = 0x01U,.iConfiguration       = 0x00U,.bmAttributes         = 0x80U,.bMaxPower            = 0x32U},.cmd_itf = {.header = {.bLength         = sizeof(usb_desc_itf), .bDescriptorType = USB_DESCTYPE_ITF },.bInterfaceNumber     = 0x00U,.bAlternateSetting    = 0x00U,.bNumEndpoints        = 0x01U,.bInterfaceClass      = USB_CLASS_CDC,.bInterfaceSubClass   = USB_CDC_SUBCLASS_ACM,.bInterfaceProtocol   = USB_CDC_PROTOCOL_AT,.iInterface           = 0x00U},.cdc_header = {.header ={.bLength         = sizeof(usb_desc_header_func), .bDescriptorType = USB_DESCTYPE_CS_INTERFACE},.bDescriptorSubtype  = 0x00U,.bcdCDC              = 0x0110U},.cdc_call_managment = {.header = {.bLength         = sizeof(usb_desc_call_managment_func), .bDescriptorType = USB_DESCTYPE_CS_INTERFACE},.bDescriptorSubtype  = 0x01U,.bmCapabilities      = 0x00U,.bDataInterface      = 0x01U},.cdc_acm = {.header = {.bLength         = sizeof(usb_desc_acm_func), .bDescriptorType = USB_DESCTYPE_CS_INTERFACE},.bDescriptorSubtype  = 0x02U,.bmCapabilities      = 0x02U,},.cdc_union = {.header = {.bLength         = sizeof(usb_desc_union_func), .bDescriptorType = USB_DESCTYPE_CS_INTERFACE},.bDescriptorSubtype  = 0x06U,.bMasterInterface    = 0x00U,.bSlaveInterface0    = 0x01U,},.cdc_cmd_endpoint = {.header = {.bLength         = sizeof(usb_desc_ep), .bDescriptorType = USB_DESCTYPE_EP,},.bEndpointAddress    = CDC_CMD_EP,.bmAttributes        = USB_EP_ATTR_INT,.wMaxPacketSize      = USB_CDC_CMD_PACKET_SIZE,.bInterval           = 0x0AU},.cdc_data_interface = {.header = {.bLength         = sizeof(usb_desc_itf), .bDescriptorType = USB_DESCTYPE_ITF,},.bInterfaceNumber    = 0x01U,.bAlternateSetting   = 0x00U,.bNumEndpoints       = 0x02U,.bInterfaceClass     = USB_CLASS_DATA,.bInterfaceSubClass  = 0x00U,.bInterfaceProtocol  = USB_CDC_PROTOCOL_NONE,.iInterface          = 0x00U},.cdc_out_endpoint = {.header = {.bLength         = sizeof(usb_desc_ep), .bDescriptorType = USB_DESCTYPE_EP, },.bEndpointAddress     = CDC_DATA_OUT_EP,.bmAttributes         = USB_EP_ATTR_BULK,.wMaxPacketSize       = USB_CDC_DATA_PACKET_SIZE,.bInterval            = 0x00U},.cdc_in_endpoint = {.header = {.bLength         = sizeof(usb_desc_ep), .bDescriptorType = USB_DESCTYPE_EP },.bEndpointAddress     = CDC_DATA_IN_EP,.bmAttributes         = USB_EP_ATTR_BULK,.wMaxPacketSize       = USB_CDC_DATA_PACKET_SIZE,.bInterval            = 0x00U}
};

为了实现CDC设备类,设备需要支持一些设备类专用请求,这些类专用请求的处理在cdc_acm_req ()函数中,该函数的定义如下所示,其中SET_LINE_CODING命令用于响应主机向设备发送设备配置,包括波特率、停止位、字符位数等,收到的数据保存在noti_bu内。GET_LINE_CODING命令用于主机请求设备当前的波特率、停止位、奇偶校验位和字符位数,但在本例程中,主机并未请求该命令,所以设备所设置的串口数据并没有作用,主机可以选择任意波特率与设备进行通信。

static uint8_t cdc_acm_req (usb_dev *udev, usb_req *req)
{usb_cdc_handler *cdc = (usb_cdc_handler *)udev->dev.class_data[CDC_COM_INTERFACE];usb_transc *transc = NULL;switch (req->bRequest) {case SEND_ENCAPSULATED_COMMAND:/* no operation for this driver */break;case GET_ENCAPSULATED_RESPONSE:/* no operation for this driver */break;case SET_COMM_FEATURE:/* no operation for this driver */break;case GET_COMM_FEATURE:/* no operation for this driver */break;case CLEAR_COMM_FEATURE:/* no operation for this driver */break;case SET_LINE_CODING:transc = &udev->dev.transc_out[0];/* set the value of the current command to be processed */udev->dev.class_core->alter_set = req->bRequest;/* enable EP0 prepare to receive command data packet */transc->remain_len = req->wLength;transc->xfer_buf = cdc->cmd;break;case GET_LINE_CODING:transc = &udev->dev.transc_in[0];cdc->cmd[0] = (uint8_t)(cdc->line_coding.dwDTERate);cdc->cmd[1] = (uint8_t)(cdc->line_coding.dwDTERate >> 8);cdc->cmd[2] = (uint8_t)(cdc->line_coding.dwDTERate >> 16);cdc->cmd[3] = (uint8_t)(cdc->line_coding.dwDTERate >> 24);cdc->cmd[4] = cdc->line_coding.bCharFormat;cdc->cmd[5] = cdc->line_coding.bParityType;cdc->cmd[6] = cdc->line_coding.bDataBits;transc->xfer_buf = cdc->cmd;transc->remain_len = 7U;break;case SET_CONTROL_LINE_STATE:/* no operation for this driver */break;case SEND_BREAK:/* no operation for this driver */break;default:break;}return USBD_OK;
}

数据接收
通过cdc_acm_data_receive()函数实现,该函数的程序如下所示。在该函数中,首先将packet_receive标志位设置为0,表明接下来将进行接收数据,当接收完成时,在cdc_acm_out ()函数中,将packet_receive标志位置1,表明数据接收完成。usbd_ep_recev()用于配置接收操作,利用CDC_OUT_EP端点,将接收到的数据放置在用户缓冲区中。

/*!\brief      receive CDC ACM data\param[in]  udev: pointer to USB device instance\param[out] none\retval     USB device operation status
*/
void cdc_acm_data_receive (usb_dev *udev)
{usb_cdc_handler *cdc = (usb_cdc_handler *)udev->dev.class_data[CDC_COM_INTERFACE];cdc->packet_receive = 0U;cdc->packet_sent = 0U;usbd_ep_recev(udev, CDC_DATA_OUT_EP, (uint8_t*)(cdc->data), USB_CDC_DATA_PACKET_SIZE);
}
/*!\brief      handle CDC ACM data\param[in]  udev: pointer to USB device instance\param[in]  ep_num: endpoint identifier\param[out] none\retval     USB device operation status
*/
static uint8_t cdc_acm_out (usb_dev *udev, uint8_t ep_num)
{usb_cdc_handler *cdc = (usb_cdc_handler *)udev->dev.class_data[CDC_COM_INTERFACE];cdc->packet_receive = 1U;cdc->receive_length = ((usb_core_driver *)udev)->dev.transc_out[ep_num].xfer_count;return USBD_OK;
}

数据发送
通过cdc_acm_data_send()函数实现,该函数的程序如下所示。在该函数中,首先将packet_sent标志位设置为0,表明接下来将进行发送数据,当数据发送完成时,在cdc_acm_in ()函数中,将packet_sent标志位设置为1,表明数据发送完成。usbd_ep_send ()用于配置发送操作,利用CDC_IN_EP端点,将以 cdc->data地址为起始,cdc->receive_length长度的数据发送给主机。

/*!\brief      send CDC ACM data\param[in]  udev: pointer to USB device instance\param[out] none\retval     USB device operation status
*/
void cdc_acm_data_send (usb_dev *udev)
{usb_cdc_handler *cdc = (usb_cdc_handler *)udev->dev.class_data[CDC_COM_INTERFACE];if (0U != cdc->receive_length) {cdc->packet_sent = 0U;usbd_ep_send (udev, CDC_DATA_IN_EP, (uint8_t*)(cdc->data), cdc->receive_length);cdc->receive_length = 0U;}
}
/*!\brief      handle CDC ACM data\param[in]  udev: pointer to USB device instance\param[in]  ep_num: endpoint identifier\param[out] none\retval     USB device operation status
*/
static uint8_t cdc_acm_in (usb_dev *udev, uint8_t ep_num)
{usb_transc *transc = &udev->dev.transc_in[EP_ID(ep_num)];usb_cdc_handler *cdc = (usb_cdc_handler *)udev->dev.class_data[CDC_COM_INTERFACE];if ((0U == transc->xfer_len % transc->max_len) && (0U != transc->xfer_len)) {usbd_ep_send (udev, ep_num, NULL, 0U);} else {cdc->packet_sent = 1U;}return USBD_OK;
}

六.配置一个USB虚拟串口收发例程

STLINK接GD32F407VET6开发板,STLINK接电脑USB口,5V USB线接板子与电脑。

在这里插入图片描述

主要代码

#include "gd32f4xx.h"
#include "gd32f4xx_libopt.h"
#include "systick.h"
#include "usbd_conf.h"
#include "drv_usb_hw.h"
#include "cdc_acm_core.h"
usb_core_driver cdc_acm;
int main(void)
{usb_gpio_config();usb_rcu_config();usb_timer_init();usbd_init (&cdc_acm,
#ifdef USE_USB_FSUSB_CORE_ENUM_FS,
#elif defined(USE_USB_HS)USB_CORE_ENUM_HS,
#endif /* USE_USB_FS */&cdc_desc,&cdc_class);usb_intr_config();#ifdef USE_IRC48M/* CTC peripheral clock enable */rcu_periph_clock_enable(RCU_CTC);/* CTC configure */ctc_config();while (ctc_flag_get(CTC_FLAG_CKOK) == RESET) {}
#endif /* USE_IRC48M *//* main loop */while (1) {if (USBD_CONFIGURED == cdc_acm.dev.cur_status) {if (0U == cdc_acm_check_ready(&cdc_acm)) {cdc_acm_data_receive(&cdc_acm);//接收数据} else {cdc_acm_data_send(&cdc_acm);//发送数据}}}
}/*!\brief      receive CDC ACM data\param[in]  udev: pointer to USB device instance\param[out] none\retval     USB device operation status
*/
void cdc_acm_data_receive (usb_dev *udev)
{usb_cdc_handler *cdc = (usb_cdc_handler *)udev->dev.class_data[CDC_COM_INTERFACE];cdc->packet_receive = 0U;cdc->packet_sent = 0U;usbd_ep_recev(udev, CDC_DATA_OUT_EP, (uint8_t*)(cdc->data), USB_CDC_DATA_PACKET_SIZE);
}/*!\brief      send CDC ACM data\param[in]  udev: pointer to USB device instance\param[out] none\retval     USB device operation status
*/
void cdc_acm_data_send (usb_dev *udev)
{usb_cdc_handler *cdc = (usb_cdc_handler *)udev->dev.class_data[CDC_COM_INTERFACE];if (0U != cdc->receive_length) {cdc->packet_sent = 0U;usbd_ep_send (udev, CDC_DATA_IN_EP, (uint8_t*)(cdc->data), cdc->receive_length);cdc->receive_length = 0U;}
}

实验效果
下载完程序,用 USB 线接板子USB 口,再接电脑,打开电脑上串口调试器,9600 波特率,8 位数据,无校验,发送 HELLOWORLD,板子就会返回 HELLOWORLD。
在这里插入图片描述

七.工程源代码下载

源代码下载链接如下:
CSDN

八.小结

USB虚拟串口可以实现与电脑的通信,还可以实现与外部设备的通信,广泛应用于工业控制、智能家居、智能硬件等领域。

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

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

相关文章

MySQL 主从配置超详细教程

文章目录 前言一、安装 MySQL二、主服务器(Master)配置三、从服务器(Slave)配置四、测试主从复制五、注意事项 前言 MySQL 主从配置是一种实用的数据库架构,主服务器处理写入操作,从服务器负责只读操作&am…

Python爬虫实战:获取百度学术专题文献数据并分析,为读者课题研究做参考

一、引言 在信息爆炸的当下,学术研究需要大量相关资料支撑。百度学术作为重要学术资源平台,蕴含丰富学术文献。利用爬虫技术获取百度学术特定主题文章数据,能为学术研究提供全面、及时信息。本研究旨在用 Python 实现对百度学术 “主题爬虫” 相关文章的爬取,并对数据深入…

手撕基于AMQP协议的简易消息队列-6(服务端模块的编写)

在MQServer中编写服务端模块代码 在MQServer中编写makefile文件来编译服务端模块 .PHONY: server CFLAG -I../ThirdLib/lib/include LFLAG -L../ThirdLib/lib/lib -lgtest -lprotobuf -lsqlite3 -pthread -lmuduo_net -lmuduo_base -lz server:server.cpp ../MQCommon/messag…

linux tar命令详解。压缩格式对比

1.压缩格式对比 压缩格式命令选项文件扩展名压缩率速度无压缩-cvf.tar无最快gzip-czvf.tar.gz中等较快bzip2-cjvf.tar.bz2较高较慢xz-cJvf.tar.xz最高最慢 9. 更多参考 【Linux基础】文件压缩tar命令指南tar压缩方式对比

解锁跨平台开发的新时代——Compose Multiplatform

解锁跨平台开发的新时代——Compose Multiplatform 在当今移动和桌面应用程序开发领域,跨平台解决方案是开发者们梦寐以求的工具。而由JetBrains打造的Compose Multiplatform正是这样一款现代UI框架,它基于Kotlin技术,为开发者构建高性能且美观的用户界面提供了极大的便利和…

【算法学习】递归、搜索与回溯算法(二)

算法学习: https://blog.csdn.net/2301_80220607/category_12922080.html?spm1001.2014.3001.5482 前言: 在(一)中我们挑了几个经典例题,已经对递归、搜索与回溯算法进行了初步讲解,今天我们来进一步讲解…

HTTP请求与缓存、页面渲染全流程

文章目录 前言**1. HTTP请求与缓存处理****缓存机制**• 强缓存(Cache-Control / Expires)• 协商缓存(Last-Modified / ETag) **2. 服务器响应与数据解析****3. HTML DOM 构建****4. CSSOM 构建****5. 渲染树(Render …

限流算法学习笔记(一)Go Rate Limiter

文章目录 1. 背景与概述1.1 什么是速率限制1.2 Go Rate Limiter 的定义与价值 2. 核心思想与设计理念2.1 令牌桶算法的基本原理2.2 惰性评估设计2.3 多种处理策略的平衡2.4 简单易用的偶发控制 3. 架构设计与组件3.1 整体架构3.2 Limiter 组件3.3 Reservation 组件3.4 Limit 类…

n8n工作流自动化平台的实操:生成统计图的两种方式

1.成果展示 1.1n8n的工作流 牵涉节点:Postgres、Code、QuickChart、Edit Fields、HTTP Request 12.显示效果 2.实操过程 2.1节点说明 2.1.1Postgres节点: 注:将明细数据进行汇总。 2.1.2code节点: 注:将 查询的数…

JavaScript中数组和对象不同遍历方法的顺序规则

在JavaScript中,不同遍历方法的顺序规则和适用场景存在显著差异。以下是主要方法的遍历顺序总结: 一、数组遍历方法 for循环 • 严格按数组索引顺序遍历(0 → length-1) • 支持break和continue中断循环 • 性能最优,…

缓存(1):三级缓存

三级缓存是指什么 我们常说的三级缓存如下: CPU三级缓存Spring三级缓存应用架构(JVM、分布式缓存、db)三级缓存 CPU 基本概念 CPU 的访问速度每 18 个月就会翻 倍,相当于每年增⻓ 60% 左右,内存的速度当然也会不断…

Android setContentView()源码分析

文章目录 Android setContentView()源码分析前提setContentView() 源码分析总结 Android setContentView()源码分析 前提 Activity 的生命周期与 ActivityThread 相关,调用 startActivity() 时,会调用 ActivityThread#performLaunchActivity()&#xf…

uniapp自定义步骤条(可二开进行调试)

前言 有一个业务需求是需要一个步骤条&#xff0c;但是发现开源的都不太合适&#xff0c;所以就自己写了一个。 开始 test.vue <template><view class"authenticateRecordDetails_container"><!-- 进度 --><view class"authenticateSte…

22、近端策略优化算法(PPO)论文笔记

近端策略优化算法&#xff08;PPO&#xff09;论文笔记 一、研究背景与目标二、**方法****3.1 策略梯度基础****3.2 信任区域方法&#xff08;TRPO&#xff09;****3.3 剪切代理目标函数&#xff08;LCLIP&#xff09;****3.4 自适应KL惩罚系数****3.5 算法实现** 三、 L CLIP…

web 自动化之 Selenium 元素定位和浏览器操作

文章目录 一、元素定位的八大方法1、基于 id/name/class/tag_name 定位2、基于 a 标签元素的链接文本定位3、基于xpath定位4、css定位 二、浏览器操作1、信息获取2、 浏览器关闭3、 浏览器控制 一、元素定位的八大方法 web 自动化测试就是通过代码对网页进行测试&#xff0c;在…

前端面经 作用域和作用域链

含义&#xff1a;JS中变量生效的区域 分类&#xff1a;全局作用域 或者 局部作用域 局部作用域&#xff1a;函数作用域 和 块级作用域ES6 全局作用域:在代码中任何地方都生效 函数中定义函数中生效&#xff0c;函数结束失效 块级作用域 使用let或const 声明 作用域链:JS查…

【C/C++】RPC与线程间通信:高效设计的关键选择

文章目录 RPC与线程间通信&#xff1a;高效设计的关键选择1 RPC 的核心用途2 线程间通信的常规方法3 RPC 用于线程间通信的潜在意义4 主要缺点与限制4.1 缺点列表4.2 展开 5 替代方案6 结论 RPC与线程间通信&#xff1a;高效设计的关键选择 在C或分布式系统设计中&#xff0c;…

两种方法求解最长公共子序列问题并输出所有解

最长公共子序列&#xff08;Longest Common Subsequence, LCS&#xff09;是动态规划领域的经典问题&#xff0c;广泛应用于生物信息学&#xff08;如DNA序列比对&#xff09;、文本差异比对&#xff08;如Git版本控制&#xff09;等领域。本文将通过​​自顶向下递归记忆化​​…

SpringBoot应急知识学习系统开发实现

概述 一个基于SpringBoot开发的应急知识学习系统&#xff0c;该系统提供了完整的用户注册、登录、知识学习与测评功能。对于开发者而言&#xff0c;这是一个值得参考的免费Java源码项目&#xff0c;可以帮助您快速构建类似的教育平台。 主要内容 5.2 注册模块的实现 系统采…

【Python 字符串】

Python 中的字符串&#xff08;str&#xff09;是用于处理文本数据的基础类型&#xff0c;具有不可变性、丰富的内置方法和灵活的操作方式。以下是 Python 字符串的核心知识点&#xff1a; 一、基础特性 定义方式&#xff1a; s1 单引号字符串 s2 "双引号字符串" s…