当前位置: 首页 > news >正文

深入解析NXP KE1x MCU:Cortex-M0+内核与SIM模块实战配置指南

1. 从零开始:为什么我们需要深入了解MCU的“大脑”与“神经中枢”

在嵌入式开发的世界里,我们常常把微控制器(MCU)比作一个微型计算机系统。对于刚入行的朋友来说,可能更熟悉如何调用HAL库或者使用RTOS的API来快速实现功能。这当然没问题,但对于追求极致性能、超低功耗,或者需要解决那些“玄学”般底层Bug的工程师而言,仅仅停留在库函数层面是远远不够的。这就好比开车,大多数人会踩油门和刹车就够了,但赛车工程师必须对发动机的每一个气缸、ECU的每一条控制曲线了如指掌。

今天,我想和你深入聊聊的是NXP Kinetis KE1x系列MCU,特别是其核心——ARM Cortex-M0+内核,以及与之紧密配合的“系统大管家”:系统集成模块(SIM)。你提供的资料,正是官方参考手册的精华部分,它像一张精密的电路图,但直接阅读难免晦涩。我的目标,就是结合我这些年调试KE系列芯片的实际经验,把这些寄存器位、中断向量、时钟树背后的设计逻辑和实战要点“翻译”出来,让你不仅知道怎么配置,更明白为什么要这样配置,以及在配置时可能会掉进哪些坑里。

选择Cortex-M0+内核的KE1x系列,通常意味着你的项目对成本敏感,但又需要32位处理器的基础性能和丰富的片上资源。它的价值在于,在有限的硅片面积和功耗预算内,通过精巧的系统集成,提供了包括定时器、通信接口、模拟外设、DMA等一整套解决方案。而SIM模块,就是这个集成系统的“配置中心”和“路由枢纽”。无论是给FlexTimer分配合适的外部时钟引脚,还是为ADC选择硬件触发源,亦或是管理整个Flash存储器的功耗状态,都需要通过SIM的寄存器来完成。理解它,是你从“MCU使用者”迈向“MCU驾驭者”的关键一步。

2. 核心架构解析:Cortex-M0+与KE1x的协同设计哲学

2.1 ARM Cortex-M0+内核的精简与高效之道

Cortex-M0+被ARM定义为面向极致能效比的入门级32位处理器。很多人一听“入门级”可能会觉得性能孱弱,这其实是个误解。它的“精简”是设计哲学上的,旨在用最少的晶体管实现最核心的功能,从而达成极高的能效比(uA/MHz)。这对于电池供电的物联网传感器、可穿戴设备、小型工业控制器而言,是至关重要的指标。

其核心采用2级流水线的冯·诺依曼架构(指令和数据共享总线)。与更复杂的哈佛架构(指令数据总线分离)相比,冯·诺依曼结构在极致面积优化上更有优势,虽然理论上可能存在“冯·诺依曼瓶颈”,但得益于Thumb-2指令集的高代码密度和单周期32位乘法器等硬件加速单元,M0+在实际应用中表现非常出色。Thumb-2指令集是ARM的“神来之笔”,它混合了16位和32位指令,使得代码密度可以媲美甚至超过传统的8位/16位MCU,同时又能享受32位处理器在数据处理和地址寻址方面的全部优势。

KE1x系列完整搭载了M0+的增强特性包:

  • 单周期I/O端口:这是KE1x的一个亮点。对于需要快速翻转GPIO(例如软件模拟精密时序、驱动LED矩阵)的应用,这个功能能带来显著的速度提升。它允许对特定地址区域的访问在一个时钟周期内完成,而无需经过总线矩阵的仲裁延迟。
  • 嵌套向量中断控制器(NVIC):支持最多32个外部中断(IRQ)和4级优先级。虽然优先级级数不多,但对于大多数嵌入式应用已经足够。NVIC的“尾链”技术可以在中断连续发生时,省去不必要的现场保存与恢复开销,进一步降低中断延迟。
  • 微跟踪缓冲区(MTB):这是一个成本极低的调试功能。在没有昂贵Trace调试器的条件下,MTB可以循环记录最近执行的分支指令地址,对于分析死机、跑飞等问题的最后执行路径非常有帮助。
  • 唤醒中断控制器(WIC):在深度睡眠模式下,CPU和大部分逻辑时钟都已关闭,但WIC可以基于外部中断事件唤醒整个系统,这是实现超低功耗待机的关键技术。

2.2 KE1x的系统模块集成:不只是外设的简单堆砌

看一个MCU是否设计得优秀,不能只看它集成了多少个外设,更要看这些外设是如何被高效组织和管理的。KE1x的模块化设计就体现了这一点。从你提供的框图可以看出,其模块被清晰地分为几大类:

  1. 核心与系统模块:包括Cortex-M0+核心、系统集成模块(SIM)、电源模式控制器(SMC)、直接内存访问控制器(eDMA)及其多路复用器(DMAMUX)、看门狗(WDOG)等。这是系统的“大脑”和“脊髓”。
  2. 存储与时钟:包含程序Flash、RAM、系统时钟发生器(SCG)及各时钟源(FIRC, SIRC, LPFLL, OSC等)。这是系统的“记忆”和“心跳”。
  3. 模拟与定时模块:如12位ADC、比较器(CMP)、FlexTimer(FTM)、低功耗定时器(LPIT, LPTMR)、实时时钟(RTC)。这是系统的“感官”和“节拍器”。
  4. 通信与人机接口:如LPUART、LPSPI、LPI2C、FlexIO、GPIO、触摸感应接口(TSI)。这是系统与外界“对话”的“嘴巴和耳朵”。

所有这些模块,都通过一个名为AXBS-Lite的交叉开关AIPS-Lite外设桥连接到核心的AHB-Lite总线上。这种结构的好处是,当多个总线主设备(如CPU、DMA)同时需要访问不同从设备(如Flash、UART)时,交叉开关可以并行处理,减少总线冲突,提高整体吞吐量。而SIM模块,则扮演着为这个复杂系统进行“初始布线”和“动态配置”的角色。

3. 中断系统的实战指南:从向量表到NVIC寄存器精准配置

中断是嵌入式系统实时性的生命线。KE1x的中断系统基于Cortex-M0+的NVIC,但需要结合芯片具体的模块进行配置。

3.1 中断向量表深度解读

你提供的Table 4-2. Interrupt vector assignments是开发中断功能的“地图”。我们以LPTMR(低功耗定时器)中断为例,来学习如何看懂这张表并实际使用它。

首先,在向量表中找到LPTMR所在行(假设它在向量号74,IRQ 58,这只是示例,具体需查对应型号数据手册)。关键信息有:

  • 向量号(Vector): 74。这是ARM内核用于索引中断服务程序(ISR)入口地址的编号。
  • IRQ号: 58。这是NVIC层面管理的中断源编号,等于向量号减16(因为前16个向量被内核异常占用,如HardFault、SysTick等)。
  • NVIC IPR寄存器号: 14。这个数字用于定位优先级寄存器。

在代码中,我们通常不直接使用这些数字,而是使用芯片厂商提供的SDK中的宏定义,例如IRQn_Type枚举和NVIC_EnableIRQ()函数。但理解其底层计算原理,对于排查底层问题或在没有SDK的环境下编程至关重要。

3.2 NVIC寄存器位计算与手动配置示例

假设我们需要手动配置LPTMR中断(IRQ=58),步骤如下:

步骤1:使能中断(设置NVIC_ISER)NVIC的使能、清除、挂起、优先级寄存器都是按组管理的。每组寄存器管理32个IRQ。

  • 计算组号:IRQ / 32 = 58 / 32 = 1(整数除法)。所以我们需要操作NVIC_ISER1寄存器。
  • 计算组内位号:IRQ % 32 = 58 % 32 = 26。所以是NVIC_ISER1寄存器的第26位。
  • 在C语言中,通常这样操作:
    // 方法1:使用位操作 NVIC->ISER[1] = (1UL << 26); // 方法2:使用CMSIS-Core标准函数(推荐) NVIC_EnableIRQ(LPTMR0_IRQn); // LPTMR0_IRQn 就是SDK定义好的IRQ号宏

步骤2:设置中断优先级(设置NVIC_IPR)KE1x的NVIC支持4个优先级(0-3,0为最高)。优先级寄存器NVIC_IPR每8位(一个字节)控制一个IRQ的优先级,但只使用最高2位。

  • 计算优先级寄存器号:IRQ / 4 = 58 / 4 = 14。所以我们需要操作NVIC_IPR14
  • 计算在该寄存器内的起始位:8 * (IRQ % 4) + 4 = 8 * (58 % 4) + 4 = 8*2 + 4 = 20。所以优先级字段占据NVIC_IPR14[21:20]这两位。
  • 假设我们要设置优先级为2(二进制10b):
    // 先清除该字段,再设置 NVIC->IPR[14] &= ~(0x3UL << 20); // 清除21:20位 NVIC->IPR[14] |= (0x2UL << 20); // 设置为优先级2 // 同样,更推荐使用CMSIS函数 NVIC_SetPriority(LPTMR0_IRQn, 2);

注意:在KE1x中,中断优先级仅在多个中断同时 pending 时用于决定谁先执行。一个高优先级中断并不能抢占一个正在执行的低优先级中断,除非低优先级中断自己主动“让出”(如调用了__WFI()__WFE())。这是Cortex-M0+与M3/M4的一个重要区别,M0+不支持中断的嵌套抢占(除非是更高优先级的异常,如NMI)。

3.3 非屏蔽中断(NMI)的硬件连接

NMI是一个特殊的中断,它不可被全局中断屏蔽指令(如__disable_irq())关闭。在KE1x上,NMI通常映射到一个特定的GPIO引脚上。关键点在于:这个引脚必须通过Port控制模块的复用功能配置,将其设置为NMI功能,而不仅仅是配置为普通输入。如果配置错误,即使外部信号变化,也无法触发NMI。

4. 系统集成模块(SIM)实战配置详解

SIM模块是芯片上电后,除了时钟初始化之外,最早需要打交道的模块之一。它控制着芯片的“身份信息”和许多全局性路由。

4.1 芯片识别与资源确认(SIM_SDID, SIM_FCFG1/2)

在写固件时,尤其是制作通用Bootloader或需要兼容不同Flash/RAM大小的产品型号时,动态识别芯片信息非常有用。

  • SIM_SDID(系统设备标识寄存器):这是一个只读寄存器,在上电时从Flash信息区加载。我们可以从中读出:

    • FAMILYID: 确认是KE1x家族。
    • PINID: 识别芯片引脚数(48/64/100 pin),这对于根据封装动态配置引脚复用非常有用。
    • RAMSIZE: 读出RAM大小。例如,0111b代表48KB,1000b代表96KB。你的代码可以据此决定堆栈大小或动态内存池的分配。
    uint32_t sdid = SIM->SDID; uint8_t ramSizeCode = (sdid >> 16) & 0xF; uint32_t actualRamSizeKb; switch(ramSizeCode) { case 0x07: actualRamSizeKb = 48; break; case 0x08: actualRamSizeKb = 96; break; default: actualRamSizeKb = 0; break; // 未知型号 }
  • SIM_FCFG1(Flash配置寄存器1)

    • PFSIZE字段:指示程序Flash的容量。例如1001b对应256KB,1011b对应512KB。Bootloader可以利用这个信息计算应用程序的合法起始地址。
    • FLASHDOZE位:这是一个重要的低功耗控制位。当CPU进入Doze模式(一种低功耗运行模式)时,如果设置此位,Flash会被断电以进一步节能。重要提示:在VLPR/VLPS等超低功耗模式下,应确保此位为0,否则访问Flash会导致总线错误。因为在这些模式下,Flash需要保持供电以响应可能的访问。
    • FLASHDIS位:直接禁用Flash。除非你将中断向量表和关键代码全部搬移到RAM中运行,否则绝对不要轻易开启此位。一旦开启,CPU从Flash取指就会立刻触发硬件错误。
  • SIM_FCFG2(Flash配置寄存器2)

    • MAXADDR0/1:这两个字段与PFSIZE联动,定义了Flash Block 0(程序区)和Block 1(数据区,如果存在)的结束地址。这对于实现双Bank Flash的在线升级(OTA)功能至关重要。PFLASHSWAP位则指示当前哪一块Bank处于激活状态。

4.2 外设时钟与信号路由的“接线板”(SIM_CHIPCTL, SIM_FTMOPTx, SIM_ADCOPT)

这是SIM模块最“灵动”的部分,它允许你将某些外设功能映射到不同的物理引脚或内部信号源上,极大地增强了设计的灵活性。

1. 时钟输出配置(SIM_CHIPCTL)CLKOUTSELCLKOUTDIV可以让你将内部某个时钟(如内核时钟、慢速IRC、LPO等)分频后从特定的CLKOUT引脚输出。这个功能在调试时非常实用:

  • 用途1:用示波器测量实际运行的系统时钟频率,验证时钟配置是否正确。
  • 用途2:为板级其他芯片提供同步时钟源。
  • 注意:需要同时将对应的引脚复用功能设置为CLKOUT。

2. FlexTimer高级路由配置(SIM_FTMOPT0/1)FTM(FlexTimer)是KE1x上非常强大的定时器/PWM模块。SIM模块允许你精细地配置其输入输出。

  • 外部时钟选择(SIM_FTMOPT0[FTMxCLKSEL]):每个FTM模块都可以选择使用哪个外部引脚(TCLK0/1/2)作为计数器时钟。这允许你将一个高精度的外部时钟源直接提供给FTM,用于精确的频率测量或PWM生成。

    实操陷阱:仅仅在SIM模块里选择时钟源是不够的!你必须同时通过Port控制模块,将你选定的那个物理引脚(例如PTA0)的复用功能配置为FTM0_CLKIN很多工程师调试时发现外部时钟不工作,问题就出在这里,只做了一半的配置。

  • 故障输入选择(SIM_FTMOPT0[FTM0FLTxSEL]):FTM的故障输入可以来自外部引脚,也可以来自内部触发复用器(TRGMUX)的输出。后者可以实现用另一个定时器、比较器甚至ADC的触发信号来快速关断PWM输出,实现硬件级联锁保护,响应速度远快于软件中断。
  • 通道输入选择与调制(SIM_FTMOPT1)
    • FTMxCHySEL:可以将FTM通道的输入源从默认的自身引脚,切换到内部比较器(CMP)的输出。这样可以用模拟比较结果直接触发定时器捕获,实现无软件延迟的过零检测或脉冲宽度监控。
    • FTM0_OUTSEL:这是一个强大的功能,允许FTM0的每个PWM输出通道,用FTM1_CH1的信号进行“调制”。简单说,就是FTM1_CH1的输出可以作为FTM0输出的使能门控。这在实现复杂PWM波形、死区时间控制或同步多个定时器时非常有用。

3. ADC触发源配置(SIM_ADCOPT)KE1x的ADC支持硬件触发启动转换,这对于定时采样或与其它外设(如FTM、PWT)同步至关重要。

  • ADC0TRGSEL:选择ADC的硬件触发源是来自TRGMUX。TRGMUX又是一个强大的信号路由矩阵,可以将几乎任何外设的事件(如定时器溢出、PWM匹配、GPIO边沿)作为ADC的触发源。
  • ADC0PRETRGSELADC0SWPRETRG:这涉及到ADC的“预触发”功能。在某些序列转换模式下,你可以配置一个“预触发”事件来提前准备ADC,然后由主“触发”事件真正启动转换。这可以进一步减少触发延迟,实现更精确的同步。注意:软件预触发源(ADC0SWPRETRG)需要配合ADC模块自身的控制寄存器一起使用。

4.3 唯一标识符(UID)的应用与安全考量

SIM_UIDH, SIM_UIDMH, SIM_UIDML, SIM_UIDL这四个只读寄存器共同组成了一个128位的全球唯一芯片标识符。这个UID的用途很广:

  1. 软件加密与授权:在量产产品中,可以将UID作为密钥生成的一部分,实现“一机一密”,防止固件被克隆。
  2. 设备识别:在组网应用中(如CAN网络),可以用UID的后几位作为设备的默认网络ID或MAC地址。
  3. 生产追溯:在出厂测试时,将UID与测试结果绑定,便于后期质量追踪。

重要安全提示:UID是只读的,且理论上全球唯一,非常适合用于安全密钥的派生。但是,切勿直接使用UID作为密钥!应该使用一个安全的密钥派生函数(KDF),将UID和另一个主密钥(Master Secret)结合起来生成设备专属密钥。这样可以避免UID泄露导致的安全模型被直接攻破。

5. 开发中的常见问题与调试技巧实录

5.1 问题:配置了外设时钟,但外设完全不工作

  • 排查思路
    1. 时钟门控:首先检查外设对应的PCC(外设时钟控制器)寄存器,确保该外设的时钟使能位(PCCxx[CG])已经打开。这是最容易被忽略的一步。
    2. 时钟源:检查SCG(系统时钟发生器)配置,确认你打算给外设使用的时钟源(例如,总线时钟BUS_CLK、内核时钟CORE_CLK)是否已经正确配置并稳定运行。
    3. SIM路由:如果外设涉及特殊时钟或信号路由(如FTM外部时钟、ADC触发源),回头仔细检查SIM模块中对应的SIM_FTMOPT0SIM_ADCOPT等寄存器配置是否正确。
    4. 引脚复用:确认外设功能对应的物理引脚,是否已通过Port模块的PCR寄存器正确配置为相应的复用功能(ALT mode),而不仅仅是GPIO。
    5. 复位状态:有些外设有独立的软件复位控制位(例如FTM_SC[RESET])。在初始化序列的最后,可能需要一个“解除复位”的操作。

5.2 问题:中断服务函数(ISR)已定义且已使能,但无法进入

  • 排查思路
    1. 向量表重定位:如果你使用了Bootloader,或者将程序链接到了非默认的Flash地址,必须正确设置Cortex-M0+的VTOR(向量表偏移寄存器)。否则,CPU在响应中断时,会跑到错误的地址去取ISR入口。
    2. 中断屏蔽
      • 全局中断是否开启?(__enable_irq())。
      • 该外设自身的中断使能位是否打开?(例如FTM_SC[TOIE]使能溢出中断)。
      • NVIC级别是否使能?(NVIC_EnableIRQ())。
    3. 中断标志:硬件中断标志是否被置起?有些外设需要先清除某个状态标志,才能产生新的中断。也有可能是中断标志在ISR内部没有被清除,导致连续触发一次后就不再触发。
    4. 优先级误区:如前所述,Cortex-M0+的中断优先级不用于抢占,只用于仲裁同时发生的Pending请求。检查是否有更高优先级的异常(如HardFault)一直处于活跃状态,阻塞了你的IRQ。
    5. 使用调试器:查看NVIC的ISPR(中断挂起寄存器),可以看到哪些中断正在向CPU请求响应。这是一个非常直接的诊断工具。

5.3 问题:低功耗模式下电流降不下去

  • 排查思路
    1. 外设时钟:进入低功耗模式前,确认所有不用的外设时钟都已通过PCC寄存器关闭(CG=1)。
    2. 引脚泄漏:未使用的GPIO引脚应配置为禁止上下拉的模拟输入模式,或者设置为输出并驱动到一个确定的电平(高或低),避免浮空输入引起漏电流。
    3. SIM_FCFG1[FLASHDOZE]:在进入VLPR/VLPS等模式时,确保此位为0。在进入普通的WAIT/STOP模式时,可根据需要设置此位以降低Flash功耗,但要确保唤醒后没有立即访问Flash的代码。
    4. 调试接口:如果调试器(如J-Link)仍然连接,SWD接口可能会阻止芯片进入最深的低功耗模式。尝试断开调试器进行电流测量。

5.4 利用MTB进行“最后时刻”的代码追踪

当系统发生死机或跑飞,而你又没有昂贵的实时跟踪调试器时,MTB是你的好帮手。配置MTB的缓冲区(通常在RAM中划出一小块),使其记录程序执行的分支指令(B, BL, BX等)。当系统故障后,通过调试器读取MTB缓冲区,你能看到故障发生前最后执行的几十条分支指令的地址。结合反汇编工具,可以大致还原出程序的执行路径,对于定位在哪个函数、甚至哪条条件分支后出现问题非常有帮助。KE1x的MTB配置相对简单,主要就是设置缓冲基地址和控制寄存器使能。

6. 寄存器访问的“规矩”与编程实践

你提供的文档开头关于寄存器访问约定的部分,是编写稳定底层驱动的基础,必须严格遵守。

  • 保留位(Reserved):对于标记为“Reserved”的位域,必须保持其复位值不变。通常的做法是使用“读-修改-写”操作:REG = (REG & ~MASK) | VALUE;确保只修改你需要改动的位。
  • 写1清零(w1c):这是一种常见的状态标志位设计。要清除一个w1c位,你必须向该位写入1,写入0无效。例如,清除一个中断标志:IF_REG = (1 << FLAG_BIT);切勿使用IF_REG &= ~(1 << FLAG_BIT);这种方式,这可能会清除其他位或产生不可预期的后果。
  • 只读/只写:对于只读寄存器,写操作会被总线忽略或产生错误。对于只写寄存器,读回的值是未定义的(可能是0,也可能是任意值)。不要试图去读取只写寄存器来验证配置。
  • 访问权限:文档中特别提到,SIM寄存器只能在特权模式下写入。这意味着如果你的工程里使用了RTOS,并且创建了用户模式的任务,这些任务将无法直接修改SIM寄存器。在初始化阶段,CPU处于特权模式,所以没问题。如果后期需要修改,则必须通过SVC(系统服务调用)异常等方式,切换到特权模式执行。

理解ARM Cortex-M0+内核与NXP Kinetis KE1x系列SIM模块的协同工作方式,是释放这款MCU全部潜力的钥匙。它不仅仅是记忆寄存器地址和位定义,更是理解芯片设计者如何通过灵活的配置矩阵,将一个个独立的外设模块编织成一个高效、可靠的片上系统。从中断向量表的精准映射,到通过SIM实现外设间硬件级联动的巧妙设计,每一步都蕴含着对实时性、功耗和可靠性的深度考量。在实际项目中,我习惯于在系统初始化函数中,集中检查和配置这些关键的SIM选项,并添加详细的注释说明每个配置的意图,这为后续的调试和功能扩展留下了清晰的路标。当你掌握了这些,面对KE1x这颗芯片,你将不再是一个被动的用户,而是一个能够精准调配其内部资源的驾驭者。

http://www.jsqmd.com/news/1007838/

相关文章:

  • 百度文库真的有坑吗?9700万AI用户用实力给出答案 - 品牌测评鉴赏家
  • 2022年CSP-X复赛真题及题解(T2:移动棋子)
  • 上海瓷砖空鼓翘边拱起怎么解决?2026 专业修复方法攻略 - 苏易修缮
  • 技术解析:Synology硬盘兼容性数据库扩展方案
  • 2026云南正规导游费用预算说明TOP3,纯玩无购物,避坑参考 - 旅游发布
  • 如何轻松下载B站视频?这个实用工具让你告别缓冲焦虑
  • Windows版Redis可视化工具:告别命令行,拥抱高效Redis管理新体验
  • AI语音助手在家庭健康监护中的落地实践与安全边界
  • 用C++搞定GESP四级图像压缩题:从读不懂题到AC的保姆级思路拆解
  • HC08 MON08接口调试全解析:从原理到实战避坑指南
  • 原来江西这些知名的AI搜索流量公司,哪家才靠谱呢? - 资讯速览
  • 好用的智能写作工具汇总,适配日常办公与内容创作 - 品牌测评鉴赏家
  • STM32 TIM控制器
  • MoocDownloader完整指南:5分钟掌握.NET实现的MOOC课程离线下载技术
  • Pandas学习第二课—DataFrame
  • GPT-4数据可靠性风险与工程级验证四步法
  • 告别熬夜填表!5款表格自动化神器实测,小白也能零代码搞定 - 品牌测评鉴赏家
  • MC9328MXS USB设备控制器:从寄存器配置到数据传输实战
  • MC9328MXL I2C与SSI寄存器级编程:从原理到实战避坑指南
  • 【科研快报】与其消灭AI幻觉,不如教它说“我不确定“——谷歌团队提出AI元认知新范式
  • RTIC运行时完整性检查:硬件寄存器配置与安全实践详解
  • “提示词”根本不算技能!程序员真正靠AI赚钱的3个硬核误区揭秘
  • 基于PLC的分拣存储控制系统设计23(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码
  • 告别手动制表:3种办公场景下的效率工具选择思路 - 品牌测评鉴赏家
  • 揭阳亲测!汽车贴膜品牌老店推荐首推揭阳市榕城区东升志明汽车用品 - 资讯速览
  • BERTScore终极指南:如何用语义相似度精准评估文本生成质量?
  • (十)多UnitId模拟:一个网关下面挂多个从站怎么测
  • MC56F825x/824x DSC双12位ADC配置与电机控制实战解析
  • 开发日志(十一):多模态菜单 RAG 系统实战
  • 26年春季学期学习记录第44天