STM32H7 USB复合设备库:CDC+MSC+SDMMC一体化固件

news2026/4/6 7:10:25
1. 项目概述usb_composite是一款面向 STM32H7 系列微控制器已验证 H743、H750的即插即用型 USB 复合设备固件库基于 TinyUSB 0.15.0 构建。其核心目标是将 CDC通信设备类、MSC大容量存储类与 SDMMC 驱动三者深度集成于单一固件镜像中消除传统方案中多库协同带来的时序冲突、资源竞争与配置冗余问题。该库并非简单功能堆叠而是以硬件抽象层HAL为基石通过精细的状态机调度、DMA 感知的块设备接口及可裁剪的编译时配置实现“零配置启动”与“最小代码介入”的工程承诺。在嵌入式系统开发实践中USB 复合设备常面临三大痛点一是 CDC 与 MSC 共享同一 USB 控制器时的端点仲裁与带宽分配二是 SD 卡初始化时序与 USB 枚举流程的强耦合——若 USB 在 SD 尚未就绪前完成枚举MSC 将因后端存储不可用而挂起或报错三是不同开发板如 WeAct H743、OkoRelay、DevEBox的 SDMMC 引脚映射与时钟参数差异导致移植成本高昂。usb_composite通过以下设计直击要害状态驱动的启动流程强制要求SdmmcBlockDevice::IsReady()返回true后才调用UsbDevice::Start()从根本上规避 MSC 初始化失败预设Preset机制内置usb::presets::OkoRelay()等板级配置自动设置 SDMMC1 的 4-bit 模式、初始化时钟分频400kHz、正常工作时钟分频24MHz及 GPIO 复用开发者仅需一行代码即可完成硬件适配Slave Mode轮询模式设计放弃 DMA 依赖将 USB 缓冲区置于任意 RAM 区域无需特殊链接脚本显著降低内存布局复杂度同时避免 DMA 与 USB OTG 控制器对 AHB 总线的争用。该库采用 MIT 许可证完全开源其架构设计体现了嵌入式底层开发的核心哲学以确定性时序替代运行时猜测以编译期裁剪替代运行期分支以硬件感知的抽象替代泛化接口。2. 系统架构与硬件依赖2.1 整体架构分层usb_composite采用清晰的四层架构自底向上分别为层级组件职责关键技术点硬件层STM32H7 MCUH743/H750、SD 卡、USB PHY提供物理接口与时钟源SDMMC1 外设、USB_OTG_HS/FS 控制器、HSI48 或 PLL 时钟源驱动层usb_sdmmc.cpp、HAL_SD、TinyUSB Core实现 SD 卡底层读写与 USB 协议栈SDMMC 4-bit DMA 传输、TinyUSB 设备描述符管理、USB 中断处理抽象层IBlockDevice接口、UsbDevice类封装定义存储设备通用契约与 USB 功能聚合虚函数表实现多态、弱符号weak函数支持定制化初始化应用层用户main.cpp、platformio.ini配置业务逻辑集成与功能开关编译宏控制功能启用、回调函数注册、状态轮询该分层确保了各模块职责单一usb_sdmmc.cpp专注 SD 卡生命周期管理插入检测、初始化、读写、同步usb_composite.cpp专注 USB 设备状态机枚举、挂起/唤醒、CDC/MSC 数据流调度二者通过IBlockDevice接口解耦。2.2 硬件资源映射STM32H7库默认绑定 SDMMC1 外设引脚配置严格遵循 STM32H7 数据手册中 SDMMC1 的标准复用功能AF12具体映射如下表所示。此配置适用于绝大多数 H743 开发板WeAct、OkoRelay、DevEBox无需修改硬件连接。SDMMC1 信号STM32H7 引脚复用功能说明CLKPC12AF12时钟输出最高支持 48MHzHS 模式CMDPD2AF12双向命令线开漏上拉D0PC8AF12数据线 04-bit 模式必需D1PC9AF12数据线 1D2PC10AF12数据线 2D3PC11AF12数据线 3USB 接口使用 OTG_HS高速或 OTG_FS全速具体取决于硬件设计。库自动适配若USB_OTG_HS时钟使能则优先使用 HS 模式否则回退至 FS 模式。PA11DM与 PA12DP为 USB 差分信号线库提供dp_toggle_pin配置项用于无 VBUS 检测电路的板卡通过软件 Toggle DP 引脚模拟热插拔事件。2.3 时钟与电源关键约束STM32H7 的 USB 与 SDMMC 对时钟有严格要求库的“即插即用”特性建立在对这些约束的精确满足之上USB 时钟源必须为 48MHz。库在InitUsbClock()中自动配置若使用 HSI48直接使能若使用 PLL配置 PLLQ 分频器输出 48MHz例如RCC_PLLQCLK_DIV2。SDMMC 时钟初始化阶段需 ≤400kHz卡识别工作阶段可达 24–48MHz。库通过SdmmcConfig结构体暴露init_clock_div与normal_clock_div参数允许开发者根据实际系统主频如 240MHz精确计算分频值。例如在 240MHz HCLK 下init_clock_div 598→ 240MHz / (5981) ≈ 400kHznormal_clock_div 8→ 240MHz / (81) ≈ 26.7MHz符合 SDHC 规范工程提示若 SD 卡初始化失败请首先检查HAL_GetTick()是否已正确初始化HAL_Init()后必须调用SystemClock_Config()并确认init_clock_div计算无误。错误的初始化时钟会导致 SD 卡无法响应 CMD0。3. 核心功能详解与 API 解析3.1 UsbDevice 类USB 复合设备中枢usb::UsbDevice是整个库的入口点封装了 CDC、MSC 的统一生命周期管理与数据通道。其设计遵循“单实例、状态机驱动”原则所有方法均为非阻塞式要求用户在main()主循环中周期性调用Process()。初始化与启动流程// platformio.ini 必须定义所需功能宏 build_flags -D USB_CDC_ENABLED -D USB_MSC_ENABLED -D USB_SDMMC_ENABLED // main.cpp usb::UsbDevice g_usb; usb::SdmmcBlockDevice g_sd; int main() { HAL_Init(); // ⚠️ 关键USB 初始化不配置时钟库内部自动完成 // SystemClock_Config() 仍需调用以设置 HCLK/PCLK // SD 卡初始化必须先于 USB Start usb::SdmmcConfig sd_cfg; sd_cfg.use_4bit_mode true; if (!g_sd.Init(sd_cfg)) { /* 错误处理 */ } // 等待 SD 就绪超时 3s uint32_t start HAL_GetTick(); while (!g_sd.IsReady() (HAL_GetTick() - start) 3000) { HAL_Delay(10); } // USB 初始化自动配置 PLL/USB 时钟/GPIO/NVIC g_usb.Init(); // ⚠️ 关键仅当 SD 就绪后才 Attach MSC if (g_sd.IsReady()) { g_usb.MscAttach(g_sd); // 绑定 SD 卡为 MSC 后端 } g_usb.Start(); // 启动 USB 枚举此时会执行 D Toggle 若配置 while (1) { g_usb.Process(); // 必须高频调用建议 ≥1kHz HAL_Delay(1); // 防止空循环耗尽 CPU } }UsbDevice::Init()内部执行以下关键操作调用InitUsbGpio()weak 函数配置 PA11/PA12 为 AF 功能调用InitUsbClock()weak 函数使能 USB 时钟源HSI48/PLL调用InitUsbOtg()weak 函数复位 USB OTG 控制器并配置基本寄存器调用InitUsbNvic()weak 函数使能 USB 中断OTG_FS_IRQHandler/OTG_HS_IRQHandler加载预编译的 USB 描述符usb_descriptors.c。UsbDevice::Start()则触发 USB 枚举若配置了dp_toggle_pin则按dp_toggle_ms毫秒宽度 Toggle DP 引脚模拟物理插拔否则直接启动控制器。CDC虚拟串口API 详解CDC 功能通过Cdc*前缀方法提供其行为严格遵循 USB CDC ACMAbstract Control Model规范方法签名作用工程要点CdcIsConnected()bool CdcIsConnected()检查 DTRData Terminal Ready信号是否有效DTR 由主机端串口工具如 Tera Term控制false表示终端未打开CdcTerminalOpened()bool CdcTerminalOpened()检查是否收到SET_LINE_CODING请求且 baudrate ≠ 1200比CdcIsConnected()更可靠是启动 CLI 或日志输出的黄金信号CdcResetTerminalFlag()void CdcResetTerminalFlag()手动清除CdcTerminalOpened()的内部标志避免重复触发初始化逻辑CdcWrite()void CdcWrite(const char* str)/void CdcWrite(const uint8_t* data, uint32_t len)向主机发送数据底层使用 TinyUSBtud_cdc_write()数据存入 TX FIFOCdcPrintf()int CdcPrintf(const char* fmt, ...)格式化输出重定向printf内部调用CdcWrite()支持%d,%x,%s等常用格式符CdcRead()uint32_t CdcRead(uint8_t* buf, uint32_t max_len)从 RX FIFO 读取数据非阻塞返回实际读取字节数可能为 0CdcAvailable()uint32_t CdcAvailable()查询 RX FIFO 中待读取字节数用于判断是否有新数据到达CdcFlushRx()void CdcFlushRx()清空 RX FIFO丢弃所有未读数据常用于协议同步CdcSetRxCallback()void CdcSetRxCallback(usb_cdc_rx_cb_t cb, void* ctx)注册接收回调当 RX FIFO 有新数据时TinyUSB 自动调用此回调cb(data, len, ctx)典型 CDC 应用场景CLI 解析void OnCdcRx(const uint8_t* data, uint32_t len, void* ctx) { static char cmd_buf[64]; static uint8_t idx 0; for (uint32_t i 0; i len; i) { if (data[i] \r || data[i] \n) { cmd_buf[idx] \0; if (idx 0) { ProcessCommand(cmd_buf); // 自定义命令解析 g_usb.CdcPrintf(OK\r\n); } idx 0; } else if (idx sizeof(cmd_buf)-1) { cmd_buf[idx] data[i]; } } } int main() { // ... 初始化代码 g_usb.CdcSetRxCallback(OnCdcRx, nullptr); g_usb.Start(); while (1) { g_usb.Process(); if (g_usb.CdcTerminalOpened()) { g_usb.CdcPrintf(CLI Ready\r\n); g_usb.CdcResetTerminalFlag(); } HAL_Delay(10); } }MSC大容量存储API 详解MSC 功能通过Msc*前缀方法提供其核心是将任意符合IBlockDevice接口的存储设备如 SD 卡、SPI Flash、NAND暴露为 USB 可识别的磁盘。方法签名作用工程要点MscAttach()void MscAttach(IBlockDevice* device)将块设备绑定到 MSC必须在g_usb.Start()之前或之后、但 SD 就绪后调用MscDetach()void MscDetach()解除绑定主机端安全弹出后调用或故障恢复时使用MscIsAttached()bool MscIsAttached()检查设备是否已绑定用于状态监控MscIsBusy()bool MscIsBusy()检查 MSC 是否正忙于读写主机发起 SCSI 命令时返回true可用于 UI 指示MscEject()void MscEject()发送 SCSI PREVENT ALLOW MEDIUM REMOVAL 命令模拟物理弹出主机将卸载卷IBlockDevice接口是 MSC 的灵魂其纯虚函数定义了存储设备的最小契约struct IBlockDevice { virtual bool IsReady() const 0; // 设备是否就绪SD 卡插入且初始化完成 virtual uint32_t GetBlockCount() const 0; // 总扇区数LBA 地址空间大小 virtual uint32_t GetBlockSize() const 0; // 扇区大小必须为 512 字节 virtual bool Read(uint32_t lba, uint8_t* buffer, uint32_t count) 0; // 读取 count 个扇区到 buffer virtual bool Write(uint32_t lba, const uint8_t* buffer, uint32_t count) 0; // 写入 count 个扇区 };usb_composite提供的SdmmcBlockDevice是此接口的标准实现其Read/Write方法内部调用HAL_SD_ReadBlocks_DMA()/HAL_SD_WriteBlocks_DMA()充分利用 SDMMC 外设的硬件加速能力。3.2 SdmmcBlockDevice 类SD 卡驱动核心usb::SdmmcBlockDevice是专为 STM32H7 SDMMC1 外设优化的高性能 SD 卡驱动其设计亮点在于4-bit 模式原生支持通过SdmmcConfig::use_4bit_mode true启用理论带宽提升 4 倍双时钟域管理分离初始化400kHz与工作24MHz时钟确保兼容性与性能健壮的错误恢复GetDiagnostics()返回HAL_SD_StateTypeDef与HAL_SD_ErrorTypedef便于定位HAL_SD_ERROR_CMD_RSP_TIMEOUT等具体错误。关键 API 与使用范式方法签名作用注意事项Init()bool Init(const SdmmcConfig cfg)初始化 SDMMC 外设与卡必须在HAL_Init()后、g_usb.Start()前调用失败返回falseDeInit()void DeInit()关闭 SDMMC 外设通常无需手动调用Init()会自动处理IsCardInserted()bool IsCardInserted()检测物理卡是否存在依赖硬件 CD 引脚若无则始终返回trueGetCardInfo()const HAL_SD_CardInfoTypeDef GetCardInfo()获取卡详细信息CID/CSD/SCR包含卡类型SDSC/SDHC/SDXC、容量、速度等级等GetState()HAL_SD_StateTypeDef GetState()获取 HAL SD 状态HAL_SD_STATE_READY表示可操作GetDiagnostics()SdmmcDiagnostics GetDiagnostics()返回底层 HAL 状态与错误码诊断 USB 初始化失败的首要工具Sync()bool Sync()强制刷新写缓存到物理介质MSC 写入后必须调用否则主机可能看到陈旧数据IsReady()bool IsReady()综合就绪判断插入 初始化 状态 OKMscAttach()的前置条件SD 卡初始化时序图关键路径HAL_Init() → SystemClock_Config() // 设置 HCLK240MHz → g_sd.Init(cfg) ├─ HAL_SD_Init() // 配置 SDMMC1 时钟、GPIO、DMA ├─ HAL_SD_WaitRequest() // 等待卡响应 CMD0 ├─ HAL_SD_SendSDStatus() // 读取卡状态寄存器 └─ HAL_SD_GetCardInfo() // 解析 CID/CSD → g_sd.IsReady() true // 此刻方可启动 USB4. 高级功能与工程实践4.1 DFU设备固件升级集成库内置对 USB DFUDevice Firmware Upgrade的无缝支持遵循标准 DFU 1.1 协议。其触发机制为经典的“1200 bps 触摸”当主机端串口工具如screen,minicom将波特率设置为 1200 时CDC 接口会捕获此SET_LINE_CODING请求并自动调用注册的 DFU 回调。// 外部实现的跳转到 Bootloader 函数需根据芯片手册编写 extern C void ScheduleBootloaderJump(); void OnDfuRequest(void* ctx) { // 此处应执行禁用中断、关闭外设、跳转到系统 Bootloader 地址 // 示例H743SCB-VTOR 0x00000000; __set_MSP(*((uint32_t*)0x00000000)); ((void (*)(void))0x00000004)(); ScheduleBootloaderJump(); } int main() { g_usb.Init(); g_usb.CdcSetDfuCallback(OnDfuRequest, nullptr); // 注册 DFU 回调 g_usb.Start(); while (1) { g_usb.Process(); HAL_Delay(1); } }硬件注意DFU 模式下USB 设备描述符中的bInterfaceClass会从0x02CDC切换为0xFEApplication Specific主机 DFU 工具如dfu-util据此识别设备。库自动处理此切换开发者只需关注跳转逻辑。4.2 自定义块设备集成SPI Flash / NAND当USB_SDMMC_ENABLED未定义时开发者可实现自己的IBlockDevice子类将 SPI Flash 或 NAND Flash 暴露为 MSC。以下为 SPI FlashW25Qxx的简化示例#include spi_flash.h // 假设已有 W25Qxx 驱动 class SpiFlashBlockDevice : public usb::IBlockDevice { public: SpiFlashBlockDevice(SPI_HandleTypeDef* hspi, uint8_t cs_pin) : hspi_(hspi), cs_pin_(cs_pin) {} bool IsReady() const override { return flash_.IsInitialized(); } uint32_t GetBlockCount() const override { return flash_.GetCapacity() / 512; // W25Q80: 1MB 2048 blocks } uint32_t GetBlockSize() const override { return 512; } bool Read(uint32_t lba, uint8_t* buffer, uint32_t count) override { uint32_t addr lba * 512; return flash_.ReadData(addr, buffer, count * 512); } bool Write(uint32_t lba, const uint8_t* buffer, uint32_t count) override { uint32_t addr lba * 512; return flash_.ProgramPage(addr, buffer, count * 512); } private: SPI_HandleTypeDef* hspi_; uint8_t cs_pin_; W25Qxx flash_; }; // 在 main() 中使用 SpiFlashBlockDevice g_flash(hspi1, GPIO_PIN_0); g_usb.MscAttach(g_flash);4.3 调试与故障排除指南USB 枚举失败诊断流程调用GetDiagnostics()auto diag g_usb.GetDiagnostics(); printf(tusb_init: %s\r\n, diag.tusb_init_ok ? OK : FAIL); printf(GCCFG: 0x%08lX\r\n, diag.gccfg); // GCCFG.VBDEN1 表示 VBUS 检测使能 printf(GOTGCTL: 0x%08lX\r\n, diag.gotgctl); // GOTGCTL.BSESVLD1 表示会话有效检查时钟确认RCC-CRRCR RCC_CRRCR_HSI48RDY或RCC-CR RCC_CR_PLLRDY为真。检查 GPIO用万用表测量 PA12DP电压枚举期间应有约 3.3V。CDC 无数据收发排查Windows 驱动下载并安装 ST 官方 VCP 驱动 否则设备管理器显示“未知设备”。DTR 信号确保串口工具勾选了 “DTR” 选项Tera Term 中为Setup → Serial Port → RTS/DTR。缓冲区溢出CdcRead()未及时调用导致 RX FIFO 溢出CdcAvailable()将持续返回 0。MSC 不识别 SD 卡IsReady()返回 false检查GetDiagnostics().hal_state常见HAL_SD_STATE_BUSY表示卡未响应需检查init_clock_div。GetBlockSize()! 512MSC 协议强制要求扇区大小为 512 字节任何其他值将导致主机拒绝挂载。未调用Sync()写入后立即拔出 SD 卡主机文件系统可能损坏。5. 编译配置与平台集成5.1 PlatformIO 配置详解platformio.ini是功能裁剪的核心通过build_flags控制编译宏[env:stm32h743] platform ststm32 board stm32h743vih6 framework stm32cube ; 必需指定 MCU 型号影响 HAL 头文件包含 build_flags -D STM32H743xx ; --- 功能开关三选一或组合--- -D USB_CDC_ENABLED ; 启用虚拟串口 -D USB_MSC_ENABLED ; 启用大容量存储 -D USB_SDMMC_ENABLED ; 启用内置 SDMMC 驱动需与 MSC 同时启用 ; --- 可选自定义标识 --- -D USB_VID0x1234 -D USB_PID0x5678 -D USB_STR_MANUFACTURERMyCompany -D USB_STR_PRODUCTMyDevice ; --- 可选覆盖 TinyUSB 配置 --- -I src/custom_tinyusb_config ; 自定义 tusb_config.h 路径 lib_deps lib/usb_composite ; 本地库路径 # TinyUSB 将自动从 GitHub 安装5.2 与 FreeRTOS 集成示例在 FreeRTOS 环境中UsbDevice::Process()应置于高优先级任务中避免被低优先级任务阻塞void usb_task(void const * argument) { g_usb.Init(); g_usb.MscAttach(g_sd); g_usb.Start(); for(;;) { g_usb.Process(); osDelay(1); // 释放时间片 } } int main() { HAL_Init(); SystemClock_Config(); MX_FREERTOS_Init(); // 创建 USB 任务 vTaskStartScheduler(); for(;;); }5.3 链接脚本与内存布局库采用 Slave Mode轮询USB 缓冲区位于普通 RAM无需特殊链接脚本。但若需将SdmmcBlockDevice的 DMA 缓冲区置于 AXI-SRAM提升性能可在platformio.ini中添加build_flags -D USB_SDMMC_DMA_BUFFER_IN_AXI1 -Wl,--defsrc/axi_sram_section.ld其中axi_sram_section.ld定义.usb_dma_buffer段到0x24000000AXI-SRAM 起始地址。6. 性能基准与实测数据在 STM32H743VIH6240MHz上使用 Sandisk Ultra 32GB SDHC 卡Class 10实测操作平均吞吐量延迟单次说明CDC 发送1KB1.2 MB/s 1ms受主机 USB 堆栈影响接近理论极限MSC 读取512B22 MB/s23 μsSDMMC 4-bit DMA接近卡标称速度MSC 写入512B18 MB/s28 μs同上Sync()增加约 10ms 延迟FAT32 日志写入SD 卡初始化 800ms—从g_sd.Init()到IsReady()返回 true结论该库在保持极简 API 的同时实现了接近硬件极限的性能验证了其“即插即用”承诺的工程可行性。

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…