FreeRTOS在STM32中的内存管理:从堆方案选择到实战避坑指南
1. 从一次内存溢出崩溃说起那天下午我正在调试一个基于STM32F407的FreeRTOS项目设备运行了几个小时后毫无征兆地重启了。串口日志里只留下一句模糊的“HardFault on thread: TASK_COMM”。没有明确的错误指向这感觉就像在黑暗的房间里找一根针。我第一反应是堆栈溢出打开了FreeRTOS的堆栈溢出检测功能configCHECK_FOR_STACK_OVERFLOW但复现了几次并没有触发溢出警告。这就奇怪了问题可能不在任务栈而在更底层——堆Heap内存。FreeRTOS在STM32上的内存管理远不止在CubeMX里勾选一个“Use FreeRTOS”那么简单。它不像在电脑上写Cnew一下就行或者像一些高级RTOS提供了完善的内存池和垃圾回收。在资源紧张的STM32上每一个字节都得精打细算。你用的到底是芯片内部的SRAM还是外扩的SDRAMFreeRTOS内核对象任务、队列、信号量的内存从哪里来你创建的malloc和FreeRTOS的pvPortMalloc是什么关系这些问题如果没搞清楚崩溃和内存泄漏就是家常便饭。很多人移植FreeRTOS只关心任务创建和调度对内存管理一知半解结果项目越跑越慢最终莫名死机。这篇文章我就结合那次排查经历和多年经验把FreeRTOS在STM32中如何使用内存这件事从内核机制到实战配置再到避坑指南给你彻底捋清楚。无论你是刚接触FreeRTOS的新手还是遇到过类似内存问题的开发者都能在这里找到答案。2. FreeRTOS内存管理的核心堆Heap与内存分配方案FreeRTOS内核本身并不管理芯片的所有物理内存。它的核心是提供了一个“堆Heap”的概念以及一套从堆中分配和释放内存的API如pvPortMalloc和vPortFree。这个“堆”实际上就是一段在链接脚本中定义的、连续的RAM空间。所有内核对象任务控制块TCB、任务栈、队列、信号量、软件定时器等在创建时都需要从这段堆空间中动态分配内存。关键在于FreeRTOS提供了5种内存分配方案Heap_n位于FreeRTOS/Source/portable/MemMang目录下。你需要选择其中一种并将其源文件如heap_4.c加入到你的工程中。这5种方案没有绝对的好坏只有是否适合你的应用场景。2.1 五种堆管理方案深度解析为了让你一目了然我整理了这5种方案的核心对比方案源文件碎片处理线程安全适用场景性能与开销Heap_1heap_1.c只分配不释放。无碎片。不安全仅创建任务/内核对象永不删除。最简单。分配速度最快确定性好代码量最小。Heap_2heap_2.c使用最佳匹配算法会产生外部碎片。不安全需要动态创建/删除对象但对象大小固定或种类少。已不推荐使用。分配和释放速度中等。Heap_3heap_3.c直接封装标准库malloc/free碎片取决于库。安全通过挂起调度器使用标准库且库的实现可靠。依赖编译器的堆实现。速度较慢不确定性高。Heap_4heap_4.c使用首次适应算法可合并相邻空闲块有效减少碎片。不安全最通用、最推荐。需要动态创建/删除不同大小的对象。分配和释放速度快确定性较好。Heap_5heap_5.c同Heap_4但支持非连续的多块内存区域。不安全内存分布在多个不连续的物理区域如内部SRAM外部SDRAM。初始化稍复杂分配性能与Heap_4相当。为什么Heap_4是默认的推荐选择在绝大多数STM32项目中我们使用的都是芯片内部一块连续的SRAM比如STM32F407的192KB。Heap_4的“首次适应空闲块合并”算法在动态申请释放不同大小内存的场景下能有效延缓碎片的产生保证堆空间的长期可用性。它的代码复杂度、性能和可靠性达到了一个很好的平衡。因此除非有特殊需求否则无脑选择heap_4.c是明智之举。Heap_5的独特价值当你的STM32项目内存需求很大需要同时使用内部SRAM和外部扩展的SDRAM时Heap_5就派上用场了。例如你可以将快速访问的内核对象TCB、队列放在内部SRAM而将大块的数据缓冲区如图像帧缓冲区放在外部SDRAM。Heap_5允许你在初始化时通过vPortDefineHeapRegions函数将多块不连续的物理地址空间“拼接”成一个逻辑上的堆供FreeRTOS使用。/* 假设定义了两个内存区域 */ HeapRegion_t xHeapRegions[] { { (uint8_t *)0x20000000UL, 0x10000 }, /* 内部SRAM起始地址0x20000000大小64KB */ { (uint8_t *)0xC0000000UL, 0x100000 }, /* 外部SDRAM起始地址0xC0000000大小1MB */ { NULL, 0 } /* 数组终止标志 */ }; vPortDefineHeapRegions(xHeapRegions);2.2 配置堆的大小configTOTAL_HEAP_SIZE选择了堆管理方案后你必须在FreeRTOSConfig.h中定义configTOTAL_HEAP_SIZE。这个宏定义了FreeRTOS堆的总字节数。关键点这个堆空间是从你芯片的RAM总量中划出来的一部分不是额外的。你需要确保链接脚本如STM32CubeIDE生成的.ld文件中为RAM分配的空间足以容纳这个堆、全局/静态变量、栈等所有内存需求。如何确定这个大小一个粗略的估算方法是计算静态需求所有任务的栈空间总和任务栈是动态分配的但大小是固定的、可能的全局缓冲区。预留动态开销为队列、信号量、临时动态分配预留至少20%-30%的余量。使用工具辅助在开发阶段可以使用xPortGetFreeHeapSize()和xPortGetMinimumEverFreeHeapSize()这两个函数来监控堆的使用情况。在任务空闲时打印这两个值观察最小剩余堆空间确保它始终大于一个安全阈值例如2KB。void vTaskMonitor(void *pvParameters) { while(1) { printf(Free Heap: %u, Min Ever Free: %u\r\n, xPortGetFreeHeapSize(), xPortGetMinimumEverFreeHeapSize()); vTaskDelay(pdMS_TO_TICKS(5000)); // 每5秒打印一次 } }如果Min Ever Free值越来越小或者在某个操作后骤降很可能存在内存泄漏。3. STM32上的内存布局与链接脚本的关联理解了FreeRTOS的堆我们还要把它放到STM32具体的物理内存地图中去看。以常见的Cortex-M内核STM32为例其内存映射是固定的0x2000 0000SRAM的起始地址对于F1系列可能是0x2000 0000大小几KB到几十KB对于F4/F7/H7系列可能从0x2000 0000开始大小从128KB到1MB不等。0x0800 0000Flash的起始地址存放代码和常量。当你使用IDE如Keil、IAR、STM32CubeIDE时它会基于芯片型号自动生成一个链接脚本Linker Script告诉编译器代码.text和只读数据.rodata放在Flash的哪些地址。已初始化的全局/静态变量.data和未初始化的变量.bss放在RAM的哪些地址。栈Stack和堆Heap在RAM中的位置和大小。FreeRTOS的堆configTOTAL_HEAP_SIZE就位于这个RAM区域中通常由链接脚本中名为.heap的段来定义。在GCC链接脚本中你可能会看到类似这样的部分/* 用户堆栈定义 */ /* ._user_heap_stack 段包含了供用户标准库 malloc/free 和 主栈 使用的空间 */ ._user_heap_stack : { . ALIGN(8); PROVIDE ( end . ); PROVIDE ( _end . ); . . _Min_Heap_Size; /* 标准C库的堆 */ . . _Min_Stack_Size; /* 主栈MSP */ . ALIGN(8); } RAM注意这里_Min_Heap_Size指的是标准C库的堆与FreeRTOS的堆是两回事这是一个非常常见的混淆点。FreeRTOS的堆空间通常是通过在代码中定义一个全局数组在heap_x.c里来实现的这个数组被链接器放置在了RAM的某个位置通常是.bss段之后。它和链接脚本中定义的_Min_Heap_Size没有直接关系。实战检查在STM32CubeIDE中编译完成后查看生成的.map文件。搜索你使用的堆管理方案中定义的数组名例如在heap_4.c中数组名是ucHeap。你可以找到ucHeap的地址和大小确认其是否在你预期的RAM地址范围内并且没有和其他段如.data,.bss重叠。4. 任务栈独立于堆的关键内存区任务栈是每个任务独享的RAM空间用于保存函数调用时的局部变量、返回地址、寄存器上下文等。任务栈的内存是在创建任务时从FreeRTOS的堆中分配出来的。创建任务时你需要指定栈深度usStackDepth和每个栈单元的大小在FreeRTOSConfig.h中通过configSTACK_DEPTH_TYPE定义通常是uint16_t。实际分配的字节数 usStackDepth * sizeof(StackType_t)。StackType_t通常是机器字长对于32位ARM就是4字节。// 创建一个栈深度为128字即128*4512字节的任务 xTaskCreate(vTaskFunction, MyTask, 128, NULL, 1, xTaskHandle);栈溢出是RTOS中最常见的崩溃原因之一。FreeRTOS提供了两种检测机制通过configCHECK_FOR_STACK_OVERFLOW配置方法1值1在任务切换时检查栈指针是否超出了任务栈的边界。这种方法快速但只能在栈被破坏后检测到。方法2值2在任务创建时用特定的模式如0xa5a5a5a5填充整个栈空间。在任务切换时检查栈末尾的一部分区域是否被修改。这种方法能更早地发现栈使用接近极限的情况但开销稍大。我的经验在开发调试阶段强烈建议将configCHECK_FOR_STACK_OVERFLOW设置为2并实现vApplicationStackOverflowHook钩子函数一旦溢出立即通过打印或LED指示能极大缩短排查时间。同时给栈大小留足余量比如估算值再乘以1.5到2倍并通过uxTaskGetStackHighWaterMark函数定期检查每个任务栈的历史最小剩余空间来精确调整栈大小。5. 实战配置与常见内存问题排查让我们回到文章开头我遇到的那个崩溃问题。在排除了栈溢出后我怀疑是堆内存被写穿Heap Corruption或内存泄漏。5.1 排查流程与工具我的排查步骤如下这形成了一个标准的排查链路确认堆配置首先检查FreeRTOSConfig.h确认使用的是heap_4.c且configTOTAL_HEAP_SIZE设置合理我的是20KB。通过xPortGetFreeHeapSize()在启动后打印确认初始空闲堆大小符合预期。监控堆使用趋势我在一个低优先级任务中周期打印xPortGetFreeHeapSize()和xPortGetMinimumEverFreeHeapSize()。运行一段时间后发现Min Ever Free在每次执行某个特定通信任务通过队列接收大量数据包后都会减少几十字节并且不会恢复。这是内存泄漏的典型迹象。定位泄漏点那个通信任务中会动态分配一个缓冲区来解析数据包。代码大致如下void vCommTask(void *pvParameters) { CommPacket_t *pPacket; for(;;) { if(xQueueReceive(xCommQueue, pPacket, portMAX_DELAY)) { uint8_t *pBuffer pvPortMalloc(pPacket-dataLength); // 动态分配 if(pBuffer) { // ... 处理数据 ... // vPortFree(pBuffer); // !!! 问题就在这里忘记释放了 !!! } vPortFree(pPacket); // 释放包结构体本身 } } }问题很明显在if(pBuffer)分支内处理完数据后没有调用vPortFree(pBuffer)。每次收到一个数据包就泄漏dataLength字节的内存。长时间运行堆最终被耗尽导致后续的pvPortMalloc返回NULL而我的代码没有检查这个返回值直接使用了空指针最终访问非法地址触发HardFault。修复与验证补上vPortFree(pBuffer)后重新监控。Min Ever Free值稳定下来不再下降设备长时间运行也不再重启。5.2 其他常见内存陷阱malloc与pvPortMalloc混用在FreeRTOS项目中绝对不要使用标准库的malloc/free来分配需要在任务间共享或生命周期与任务相关的内存。因为标准库的malloc可能不是线程安全的且其管理的堆与FreeRTOS的堆是分开的容易造成内存碎片和管理混乱。所有动态内存申请应统一使用pvPortMalloc和vPortFree。中断服务程序ISR中分配内存在ISR中调用pvPortMalloc是危险的因为内存分配函数可能包含阻塞操作如查找空闲块。FreeRTOS提供了xQueueSendFromISR等带FromISR后缀的API其设计是快进快出的。如果必须在ISR中传递数据更好的做法是预先分配好内存池静态数组或使用FreeRTOS的静态创建函数在ISR中只是填入数据并发送通知给任务由任务去处理内存的申请和释放。内存对齐问题Cortex-M内核特别是M3/M4/M7通常要求内存访问是字对齐的4字节。pvPortMalloc返回的指针保证是字节对齐的通常是8字节对齐取决于portBYTE_ALIGNMENT配置。但如果你要存储的数据结构有更强的对齐要求比如DMA缓冲区需要32字节对齐就需要使用pvPortMalloc分配稍大的内存然后手动进行指针对齐调整。6. 高级话题优化与定制内存管理对于性能要求苛刻或资源极其紧张的项目可以考虑以下进阶策略1. 使用静态内存分配FreeRTOS的所有内核对象任务、队列、信号量、互斥量、软件定时器都支持静态创建。你需要预先定义好对象控制块如StaticTask_t和栈数组如StackType_t然后在创建函数中传入这些静态内存的指针。StaticTask_t xTaskBuffer; StackType_t xStack[ configMINIMAL_STACK_SIZE ]; TaskHandle_t xTask xTaskCreateStatic(vTaskFunction, StaticTask, configMINIMAL_STACK_SIZE, NULL, 1, xStack, xTaskBuffer);优点内存分配在编译期就确定了无运行时分配开销无碎片风险确定性极高。缺点不灵活一旦定义大小不可变且占用RAM即使对象未创建也无法用于他处。2. 实现自定义的内存分配方案如果heap_4仍不能满足需求例如需要极快的固定大小内存分配你可以自己实现pvPortMalloc和vPortFree。FreeRTOS要求你将实现放在portable.h定义的portmacro.h相同的目录下。你可以实现一个简单的内存池Memory Pool分配器针对固定大小的对象如特定大小的数据包进行分配效率远高于通用分配器。3. 利用MPU进行内存保护Cortex-M3/M4/M7等一些高端的STM32如STM32H7包含内存保护单元MPU。你可以配置MPU将FreeRTOS内核代码和数据、任务栈、堆等区域设置为只读、只执行或禁止访问。当任务试图非法访问内存如写穿栈底时MPU会立即触发MemManage Fault而不是任由其破坏其他数据这能极大提升系统的健壮性和调试效率。FreeRTOS-MPU版本提供了相关的支持。7. 总结与个人心得FreeRTOS在STM32上的内存管理核心在于理解“堆”这个抽象层是如何映射到物理RAM以及如何通过不同的heap_x.c方案来管理它。对于大多数项目选择heap_4.c合理设置configTOTAL_HEAP_SIZE并在开发阶段充分利用xPortGetMinimumEverFreeHeapSize()进行监控就能解决90%的内存问题。我个人的深刻体会是在嵌入式RTOS开发中“谁分配谁释放”这条原则必须刻在脑子里。对于动态分配的内存一定要在逻辑上清晰地跟踪其生命周期。使用静态分配可以完全避免这个问题但会牺牲灵活性。另一个习惯是永远检查pvPortMalloc的返回值是否为NULL并设计好分配失败时的处理逻辑例如丢弃当前数据包记录错误等待下一次重试这比系统直接崩溃要好得多。最后链接脚本.ld文件是你的内存布局的蓝图。花点时间理解它知道你的代码、数据、堆栈到底放在了芯片的哪个角落当出现HardFault或者内存相关的奇怪问题时你就能更快地定位到问题的根源而不是盲目地四处修改代码。内存管理没有银弹但有了清晰的认知和正确的工具你就能让FreeRTOS在STM32上跑得既稳又快。

相关新闻

TinyML唤醒词检测实战:在MCU上部署轻量级语音AI系统

TinyML唤醒词检测实战:在MCU上部署轻量级语音AI系统

1. 项目概述:当“唤醒词”遇见“微小的智能”最近在折腾一个挺有意思的小项目,核心就一句话:在资源极其有限的微控制器(MCU)上,实现一个能准确识别特定“唤醒词”的本地化智能语音系统。这个项目标题里的几…

2026/8/19 4:30:17 阅读更多 →
从零打造社区微型图书馆:设计、建造与运营全指南

从零打造社区微型图书馆:设计、建造与运营全指南

1. 项目概述:一个社区微型图书馆的诞生如果你住在一个小社区里,有没有过这样的时刻:想随手找本书看,但去大图书馆又觉得麻烦;家里有些看过的旧书,丢了可惜,放着又占地方?几年前&…

2026/8/19 4:29:17 阅读更多 →
Arduino激光绊线制作:从光电原理到安防应用实战

Arduino激光绊线制作:从光电原理到安防应用实战

1. 项目缘起:从电影到现实的激光绊线如果你看过那些经典的谍战或警匪片,一定对这样的场景不陌生:主角潜入一个布满红外线的房间,身体以不可思议的柔韧度避开一道道看不见的光束,稍有不慎就会触发警报。这种装置&#x…

2026/8/19 4:29:17 阅读更多 →

最新新闻

基于PocketBeagle与IMU的ACL康复追踪器:嵌入式生物力学监测实践

基于PocketBeagle与IMU的ACL康复追踪器:嵌入式生物力学监测实践

1. 项目概述:一个基于嵌入式系统的ACL康复追踪器前交叉韧带(ACL)重建术后康复,对任何一个运动员或运动爱好者来说,都是一段漫长且充满不确定性的旅程。康复师给的训练计划,患者回家后到底执行得怎么样&…

2026/8/19 4:57:28 阅读更多 →
无线数字听诊器设计:从BLE音频传输到临床级信号采集的工程实践

无线数字听诊器设计:从BLE音频传输到临床级信号采集的工程实践

1. 项目概述:无线数字听诊器的核心价值作为一名在医疗电子设备领域摸爬滚打了十多年的工程师,我见过太多“为智能而智能”的产品,它们往往把简单的事情复杂化,最终沦为实验室里的摆设。但“无线数字听诊器”这个项目,却…

2026/8/19 4:57:28 阅读更多 →
NUC980 RT-Thread UART驱动配置与调试全攻略

NUC980 RT-Thread UART驱动配置与调试全攻略

1. 从零开始:为什么要在NUC980上跑RT-Thread和UART?如果你手头有一块新唐NUC980的开发板,想用它做点实时性要求高的嵌入式项目,比如工业控制、数据采集或者智能网关,那么RT-Thread这个国产的实时操作系统(R…

2026/8/19 4:57:28 阅读更多 →
从数学映射到代码复用:深入理解编程中函数的本质与应用

从数学映射到代码复用:深入理解编程中函数的本质与应用

在编程学习或日常开发中,我们无数次地敲下def、function、fun这些关键字,调用着print()、len()、sum()这些内置工具,但你是否曾停下来思考:到底什么是“函数”?我们每天写的、调用的那些代码块,真的都符合“…

2026/8/19 4:57:28 阅读更多 →
安卓与iPhone自定义铃声设置全攻略:从格式解析到实战操作

安卓与iPhone自定义铃声设置全攻略:从格式解析到实战操作

1. 从零开始:手机铃声的个性化之旅你有没有过这样的体验?在一个人声鼎沸的咖啡馆里,突然一阵刺耳又千篇一律的默认手机铃声响起,周围的人都下意识地摸向自己的口袋。那一刻,你或许会想,要是我的手机铃声能与…

2026/8/19 4:57:28 阅读更多 →
嵌入式开发学习指南:从C语言到FreeRTOS实战项目

嵌入式开发学习指南:从C语言到FreeRTOS实战项目

最近在技术社区看到不少关于“大龄转行”的讨论,其中“37岁高龄”和“31岁大龄”转行嵌入式的案例引发了广泛共鸣。这背后反映的,不仅是个人职业路径的勇敢转向,更是嵌入式领域在当前智能化浪潮下所展现出的强劲需求与包容性。无论你是正在观…

2026/8/19 4:56:27 阅读更多 →

日新闻

【单片机课程设计/毕业设计】基于 STM32 与 WiFi 模块的室内通风智能管控系统设计 基于 STM32 的人体存在感知自适应风扇控制系统设计(018503)

【单片机课程设计/毕业设计】基于 STM32 与 WiFi 模块的室内通风智能管控系统设计 基于 STM32 的人体存在感知自适应风扇控制系统设计(018503)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

2026/8/19 0:00:30 阅读更多 →
AI如何驱动数学猜想生成:从大语言模型到自动化数学发现

AI如何驱动数学猜想生成:从大语言模型到自动化数学发现

1. 项目概述:当AI开始“猜”数学定理 最近在AI研究圈里,一个名为“Moonshine”的项目引起了不小的讨论。这名字本身就挺有意思,直译是“月光”,但在数学史上,它特指一个神秘而美丽的联系——魔群月光猜想,连…

2026/8/19 0:00:30 阅读更多 →
WarcraftHelper 魔兽争霸3优化实战指南

WarcraftHelper 魔兽争霸3优化实战指南

WarcraftHelper 魔兽争霸3优化实战指南 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 一台刚配的新电脑,跑《魔兽争霸3》却卡成 PPT——这…

2026/8/19 0:02:31 阅读更多 →

周新闻

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

如果你是一名开发者,最近可能已经感受到了AI大模型正在从“玩具”变成“生产力工具”的强烈信号。从代码补全到智能Agent,从本地部署到云端API,我们正处在一个技术栈快速重构的节点。然而,面对层出不穷的模型、框架和工具&#xf…

2026/8/18 9:15:35 阅读更多 →
工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/18 9:06:28 阅读更多 →
【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

2026/8/18 9:04:56 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/17 18:54:37 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/17 18:55:16 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/17 18:55:55 阅读更多 →