news 2026/7/20 13:11:57

深入解析PRU-ICSS:工业实时协处理器的架构、内存映射与EGPIO实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深入解析PRU-ICSS:工业实时协处理器的架构、内存映射与EGPIO实战

1. PRU-ICSS架构概览与核心价值

在工业自动化、电机驱动和高速通信接口的开发中,我们常常会遇到一个核心矛盾:主处理器(如Arm Cortex-A/M系列)需要处理复杂的操作系统、网络协议和应用逻辑,而一些对时序要求极其苛刻的任务,例如精确的脉冲生成、高速串行协议解码(如EtherCAT、PROFINET IRT)或纳秒级的中断响应,却会让通用CPU捉襟见肘。这时,一种被称为“可编程实时单元”(Programmable Real-time Unit, PRU)的协处理器就成为了解决问题的关键。它不是一颗完整的CPU,而是一个高度精简、为确定性实时操作而生的“硬件加速器”。

PRU-ICSS(Industrial Communication Sub-System)是德州仪器(TI)在其Sitara系列处理器(如AM263P)中集成的一个经典子系统。它通常包含两个独立的PRU核心、共享内存、中断控制器以及一系列专为工业通信优化的外设(如IEP、ECAP、MII_RT)。其设计的精髓在于“确定性”和“直接性”。PRU运行在裸机环境下,没有缓存、没有流水线冲突(如文档所述,其执行模型为“无流水线”),指令执行时间严格可预测。更重要的是,PRU能通过其增强型GPIO(EGPIO)直接访问芯片引脚,几乎零延迟地控制或读取外部信号,这远非通过Linux GPIO驱动或复杂外设总线所能比拟。

我接触过不少从单片机转向复杂SoC的工程师,他们最初会试图用Linux用户态程序甚至内核驱动去实现一个精确的PWM或编码器接口,结果往往被系统调度、中断延迟折磨得苦不堪言。而当你把这类任务卸载到PRU上,让它直接通过R30/R31寄存器“摆弄”引脚,主处理器只需通过共享内存进行“慢速”的数据交换和逻辑控制,整个系统的实时性和可靠性会得到质的飞跃。这就是PRU-ICSS在高端嵌入式实时系统中的核心价值:它用极小的硬件资源(两个PRU核心及其相关模块),为主处理器分担了最“烫手”的实时任务,让系统架构变得清晰且高效。

2. 核心架构深度解析:为何是四总线哈佛结构?

翻开AM263P的技术参考手册,在描述PRU核心特性时,你会看到“4-Bus Harvard (1 Instruction, 3 Data)”这一行。这不仅仅是枯燥的技术参数,而是理解PRU高性能的关键。我们常见的冯·诺依曼架构共用指令和数据总线,而哈佛架构则将两者分离。PRU将这个理念发挥到了极致:它有一条独立的32位指令总线用于取指,同时还有三条32位数据总线(VBUSP控制器)。

为什么需要三条数据总线?这直接服务于其“加载/存储”架构和实时性需求。想象一个典型的控制循环:PRU需要从外部传感器(通过EGPIO或共享内存)读取数据(Load),进行一些算术或逻辑运算(在寄存器文件中完成),然后将结果输出(Store)到执行器或通过事件通知主机。三条数据总线允许PRU同时进行多项数据搬运操作,比如通过一条总线从共享RAM读取数据到寄存器,同时通过另一条总线将上一个周期的计算结果从寄存器写入到某个外设的控制寄存器。这种并行性极大地减少了数据通路上的拥塞,确保了单周期内能完成更多实质性的工作,是实现低延迟、高吞吐量实时处理的基础。

寄存器文件的设计也体现了其实时性导向。PRU拥有32个32位通用寄存器(R0-R31),但其中R30和R31被赋予了特殊使命,直接映射到外部引脚和中断事件,我们稍后会详细展开。R0通常用作零寄存器或索引寄存器。这种设计使得常用操作(如地址计算、状态判断)非常高效。地址模式支持基址+偏移、基址+立即数以及自动递增/递减,这对于处理缓冲区或数据流特别友好。

3. 双重内存映射:本地视图与全局视图的博弈

内存映射是PRU编程中最容易混淆,但也必须彻底理解的概念。PRU-ICSS提供了两套内存视图:本地内存映射全局内存映射。理解它们的区别和适用场景,是写出高效PRU代码的第一步。

3.1 本地内存映射:PRU的“高速局域网”

本地内存映射是PRU核心“眼中”的地址空间。如表7-34所示,当PRU0访问地址0x0000_0000时,它访问的是自己专属的8KB Data RAM0。访问0x0000_2000,则是访问PRU1的Data RAM1。这种设计非常巧妙,它意味着两个PRU核心可以非常高效地访问彼此的数据RAM,而无需经过复杂的外部总线仲裁,为双核协作(例如,一个负责采集,一个负责处理)提供了便利。

关键细节与实操考量:

  • 访问速度差异:通过本地地址访问内部资源(如IRAM, DRAM, INTC, CFG寄存器)是最快的,因为路径完全在PRU-ICSS内部。手册中明确提到,使用全局地址访问需要绕行到外部的CBASS0交换网络再回来,会多出几个时钟周期的延迟。在追求极致性能的循环或中断服务例程中,务必使用本地地址。
  • IRAM访问限制:一个至关重要的警告(CAUTION)是:PRU0/1的指令RAM(IRAM)仅当该PRU核心停止运行时,才能被外部主机(如Arm)访问。这意味着,你的主机程序必须在启动PRU之前,将编译好的固件二进制码加载到对应的IRAM中。一旦PRU开始运行,主机就无法再修改其指令内存,这保证了PRU代码的确定性和安全性。
  • 常量表(Constant Table)的妙用:为了优化频繁的地址加载操作,PRU内置了一个常量表(表7-37)。这个表预定义了一系列常用外设(如INTC, IEP, UART, CFG)的本地基地址。通过LBBO(加载字节、半字或字)指令配合常量表索引,可以一次性将外设基地址加载到寄存器,省去了使用MOV指令加载32位立即数的开销。例如,要访问INTC,可以直接使用基于常量表0的地址。

3.2 全局内存映射:与主机世界的“桥梁”

全局内存映射是从整个SoC(例如Arm核心)视角来看的PRU-ICSS地址空间。如表7-35所示,主机处理器将PRU-ICSS视为一个挂载在系统总线上的外设,通过一个统一的基地址(例如0x3000_0000)来偏移访问其所有内部资源。

为什么需要全局映射?

  1. 主机初始化与调试:Arm核心在启动PRU前,需要通过全局地址向PRU的IRAM加载代码,向数据RAM写入初始参数,配置CFG、IEP等模块的寄存器。
  2. 数据交换:虽然PRU可以高效地通过本地地址访问自己的内存,但主机Arm需要一种标准化的方式来读写这些共享内存区域(如32KB Shared RAM),以实现双向通信。全局映射提供了这个标准窗口。
  3. 访问其他外设:PRU不仅限于访问自身子系统。通过全局内存空间,PRU同样可以访问SoC上其他内存映射外设,这极大地扩展了其能力。例如,PRU可以直接读写另一个外设的FIFO或状态寄存器,实现更复杂的协同。

在代码中如何选择?一个简单的原则:PRU固件代码中,访问自身ICSS内的资源,一律使用本地地址(以0x0002, 0x0003开头)。与主机交换数据或访问ICSS外资源时,使用全局地址(需要从主机传递过来或通过常量表部分可编程条目计算)。混淆两者会导致访问错误或性能下降。

4. 增强型GPIO(EGPIO):超越简单的数字IO

PRU的GPIO之所以被称为“增强型”(EGPIO),是因为它远非简单的输入输出。它是一组高度可配置、支持多种专业模式的硬件接口,是PRU直接与物理世界高速交互的“手和眼”。

4.1 EGPIO模式全景

根据手册,EGPIO主要支持以下几种模式,每种模式都针对特定应用场景:

  1. 直接输入/输出模式:最基本模式,R30的bit[19:0]直接驱动引脚输出,引脚输入直接反映在R31的bit[19:0]。适用于普通的数字信号控制。
  2. 16位并行捕获模式:将16个输入引脚(DATAIN[15:0])的状态,在另一个引脚(CLOCKIN)的上升沿或下降沿瞬间锁存到R31[15:0]。这简直就是为并行数据总线或高速ADC接口量身定做的。例如,你可以用一个外部ADC的转换完成信号连接CLOCKIN,16位数据线连接DATAIN,PRU就能在精确的时刻捕获一组完整的样本。
  3. 28位串行移位输入模式:这是实现自定义串行协议(如SPI从机、特定传感器接口)的利器。数据从单一DATAIN引脚,在内部生成的采样时钟下,逐位移入一个28位移位寄存器,并映射到R31[27:0]。更强大的是,它支持起始位(SB)检测16次采样计数器(Cnt_16)。SB检测可以自动识别数据帧的开始,Cnt_16每16个时钟周期触发一次,可以连接到PRU内部中断(INTC),用于在接收完特定长度数据(如16位)后通知PRU进行读取处理,无需软件轮询。
  4. MII_RT模式:专为以太网媒体独立接口(MII)的实时操作设计,用于实现EtherCAT等工业以太网协议。在此模式下,R31[29:0]的数据由内部的MII_RT模块驱动,而非外部引脚。
  5. Sigma-Delta模式:用于连接Σ-Δ调制器,常见于高精度ADC或电机位置解码(如某些编码器接口)。
  6. 3通道外设接口模式:支持EnDat 2.2和BiSS等绝对式编码器协议。

模式选择是互斥的,通过PRU_ICSSM_GPCFG0寄存器的PRU0_GPI_MODEPRU0_GPO_MODE位域进行配置。这意味着你需要根据项目需求,在初始化阶段就确定每个PRU核心的GPIO角色。

4.2 时钟配置的艺术:两级分频器

EGPIO在串行移位模式(输入和输出)下的时钟生成非常灵活。如图7-23和手册所述,其时钟源是ICSS_x_CORE_CLK(通常为200MHz)。为了得到各种标准或自定义的波特率,PRU-ICSS采用了两个级联的小数分频器(DIV0和DIV1)。

  • 配置寄存器PRU_ICSS_GPCFG0PRU_ICSS_GPCFG1寄存器,分别对应PRU0和PRU1。每个寄存器内都有GPI_DIV0/1GPO_DIV0/1字段,用于配置输入和输出路径的分频系数。
  • 分频系数:每个分频器可配置的值不是整数,而是{1, 1.5, 2, 2.5, ..., 16}。这提供了非常精细的频率调节能力。
  • 计算公式:最终生成的移位时钟频率F_shift = F_core / (DIV0 * DIV1)

例如,手册表7-42给出了一个例子:要生成8MHz时钟,可以设置DIV0=12.5 (0x17)DIV1=2 (0x02)。计算:200MHz / (12.5 * 2) = 8MHz

实操心得:在配置分频器时,务必注意寄存器位域的范围和编码。例如,PRU0_GPI_DIV0位于PRU_ICSSM_GPCFG0_REG[7:3]这5个比特位。值0x00代表分频比1,0x01代表1.5,以此类推。你需要根据所需频率反算出最接近的分频系数组合。有时可能无法得到精确频率,需要评估时钟容差是否满足通信协议要求(如UART通常要求<2%误差)。

4.3 串行输出模式详解:乒乓缓冲与影子寄存器

串行输出模式(Shift Out)是EGPIO的另一个亮点,特别适合驱动移位寄存器、生成自定义波形或实现软件串行外设(如SPI主机)。

其核心机制是利用了两个16位影子寄存器(GPO_SH0和GPO_SH1)实现“乒乓缓冲”:

  1. PRU核心将需要发送的16位数据写入通用寄存器R30的低16位(R30[15:0])。
  2. 通过设置R30[29](LOAD_GPO_SH0)或R30[30](LOAD_GPO_SH1)为1,可以将R30[15:0]的数据加载到对应的影子寄存器中。这是一个关键技巧:你可以在当前影子寄存器正在移位输出数据的同时,准备下一帧数据并加载到另一个影子寄存器,实现无缝连续输出。
  3. 设置R30[31](ENABLE_SHIFT)为1,启动移位过程。数据会从影子寄存器的LSB或MSB(由PRU0_GPO_SHIFT_SWAP配置)开始,在DATAOUT引脚上,随着CLOCKOUT引脚的上升沿逐位输出。

模式选择

  • 自由运行时钟模式:CLOCKOUT持续产生,直到ENABLE_SHIFT被清零。适合连续流数据输出。
  • 固定时钟计数模式:通过PRU0_GPO_SHIFT_CNT寄存器设定需要移位的比特数。移完指定数量后,时钟自动停止,最后一位数据保持。这对于发送固定长度数据包(如16位、24位)非常有用,可以精确控制时序。

避坑指南:

  • 引脚复用冲突:在串行输出模式下,默认只有GPO0(DATAOUT)和GPO1(CLOCKOUT)用于移位功能。GPO[2:15]这些引脚是否也反映影子寄存器的值,取决于PRU0_GPO_SHIFT_GP_EN位的配置。如果你需要这些引脚作为普通GPIO使用,务必仔细检查此配置,避免意外驱动。
  • 启动顺序:正确的配置顺序很重要。通常建议先停止移位(清除ENABLE_SHIFT),配置好分频器和模式,加载初始数据到影子寄存器,最后再使能移位。

5. 复位、时钟与写保护:系统的看门人

PRU-ICSS的可靠性不仅体现在性能上,也体现在其精细的系统管理功能上。

5.1 复位与时钟门控

  • 热复位隔离:设备支持PRU-ICSS的热复位隔离。这意味着当SoC其他部分发生看门狗复位或软件复位时,PRU-ICSS可以选择性地保持运行状态,这对于维持关键的实时任务(如电机闭环控制)不中断至关重要。具体由复位控制管理器(RCM)控制。
  • 两级时钟门控
    1. 第一级:由RCM全局控制,可以关断PRU-ICSS内部所有时钟,用于深度省电。
    2. 第二级:用户可通过ICSS_CGR_REG寄存器,独立地使能或禁用内部特定模块的时钟,如IEP(工业以太网外设)、ECAP、UART0、INTC。这允许你在PRU运行时,动态关闭未使用模块的时钟以降低功耗。例如,如果你的应用只用到了EGPIO和共享内存,就可以关闭IEP和UART的时钟。

5.2 写保护机制

这是一个重要的安全性和可靠性特性。写保护块可以防止对关键配置寄存器、调试寄存器以及所有PRU核心指令内存(IRAM)的意外写操作。想象一下,一个跑飞的程序或错误的主机驱动试图覆盖正在运行的PRU代码区域,后果将是灾难性的。

使能写保护的步骤(解锁-配置-锁定)

  1. PROT_UNLOCK_KEY寄存器写入特定的解锁密钥。
  2. 配置PROT_CFG寄存器,选择需要保护的模块(如IRAM, DRAM0/1, 调试寄存器)。
  3. (可选)重新锁定保护,防止配置被意外修改。

这个机制在功能安全(Functional Safety)相关的应用中尤为重要,它帮助系统满足对内存和关键配置的完整性保护要求。

6. 中断与事件系统:PRU与主机的对话机制

PRU并非孤岛,它需要与主机处理器(Arm)或其他PRU核心高效通信。这主要通过R31寄存器和中断控制器(INTC)来实现。

6.1 R31:状态输入与事件输出的双面手

R31是一个多功能寄存器,其读写行为完全不同:

  • 读R31(状态输入):读取的是实时状态。Bit[29:0]来自增强型GPI引脚(取决于当前模式)。Bit[31:30]是两个来自PRU-ICSS内部INTC的中断输入。这是PRU接收外部(主机或其他外设)通知的主要方式。PRU固件可以轮���或利用这两个高位bit触发分支跳转。
  • 写R31(事件输出):写入操作不改变寄存器值,而是生成系统事件脉冲。如图7-26所示,通过向R31的bit[5](vec_valid)写1,并在bit[3:0](vec)写入一个0-15的值,可以触发PRU-ICSS INTC的16个特��系统事件(事件16-31)之一。这些事件可以被路由到主机Arm的中断控制器,从而“中断”主机,告知其PRU任务已完成或需要关注。

这是一个极其高效的IPC(进程间通信)机制。相比通过共享内存设置标志位然后主机轮询,这种硬件事件通知的延迟极低且确定性高。

6.2 INTC:中断的路由中心

PRU-ICSS内部的INTC负责管理所有中断源(包括来自PRU自身R31写入的事件、来自IEP/ECAP等外设的事件、来自主机的事件)并将它们映射到不同的输出通道,最终连接到PRU核心的R31[31:30]或主机Arm的中断线。你需要仔细配置INTC的映射表,以确保正确的中断响应路径。

7. 实战配置流程与常见问题排查

理解了原理,我们来看如何将这些知识落地。以下是一个典型的PRU EGPIO配置流程(以28位串行输入模式为例):

7.1 配置步骤

  1. 主机端准备(Arm Linux)

    • 启用PRU-ICSS的时钟和电源域(通过系统控制模块配置)。
    • 通过全局内存映射,将编译好的PRU固件(.out.bin文件)加载到目标PRU核心的IRAM中(地址如0x30034000对应PRU0 IRAM)。
    • 通过全局地址配置PRU-ICSS顶层的CFG寄存器,包括:
      • 设置PRU0_GPI_MODE为2(28-bit shift in)。
      • 配置分频器PRU0_GPI_DIV0PRU0_GPI_DIV1,计算并设置正确的分频值。
      • 配置起始位极性PRU0_GPI_SB_P(期待的第一个比特是1还是0)。
      • 清除起始位状态PRU0_GPI_SB(写1清零)。
    • 初始化INTC,将PRU可能触发的事件(如Cnt_16)映射到主机中断或PRU自身的中断输入。
  2. PRU固件侧

    • PRU代码开始运行。
    • (可选)通过常量表快速加载外设基地址到寄存器。
    • 主循环或中断服务例程中,轮询R31[29](SB标志)等待起始位。一旦检测到起始位,可以启用中断或开始监控R31[28](Cnt_16)。
    • 当Cnt_16触发(或自行计数)表明已接收16位数据时,从R31[15:0]读取数据。对于28位模式,可能需要接收两次(先低16位,再高12位)并组合。
    • 处理数据,或将数据存入共享RAM供主机读取。
    • 如需通知主机,通过写R31生成系统事件。
  3. 启动数据流

    • 主机配置完成后,最终通过设置PRU0_GPI_SHIFT_EN位为1,使能PRU的移位输入功能。
    • PRU开始监控DATAIN引脚。

7.2 常见问题与排查技巧

问题现象可能原因排查步骤
PRU无法启动,或执行第一条指令就卡住1. IRAM加载失败或数据错误。
2. PRU时钟未使能。
3. 复位状态未释放。
1. 检查主机加载程序,确认二进制文件正确写入PRU IRAM的全局地址。可用内存读取工具验证。
2. 检查系统控制模块(System Control Module)中PRU-ICSS的时钟和电源域配置是否已使能。
3. 检查PRU控制寄存器(PRUx_CONTROL)中的复位位是否已清零。
EGPIO输入无反应,读R31始终为01. 引脚复用(Pin Mux)未配置为PRU模式。
2. EGPIO模式配置错误(如想用串行输入但配置成了直接输入)。
3. 时钟分频配置错误,采样时钟过快或过慢。
4.PRU0_GPI_SHIFT_EN位未使能。
1.这是最常见的问题!务必检查芯片的引脚复用配置,将相关引脚设置为PRU0_GPI或PRU1_GPI模式。
2. 仔细核对PRU_ICSSM_GPCFG0寄存器中PRU0_GPI_MODE的值。
3. 使用示波器或逻辑分析仪测量DATAIN和可能的CLOCKIN信号,确认物理信号存在。同时计算理论采样频率,看是否合理。
4. 确认使能位已置1。
串行输出数据错位或时钟不对1. 分频系数计算错误。
2. 影子寄存器加载与移位使能时序错误。
3. LSB/MSB方向配置(PRU0_GPO_SHIFT_SWAP)错误。
1. 双检查分频器寄存器的计算和写入值。用逻辑分析仪测量实际输出的CLOCKOUT频率,与理论值对比。
2. 确保在启动移位(ENABLE_SHIFT=1)前,已经向影子寄存器加载了有效数据(LOAD_GPO_SHx=1)。
3. 根据外设要求,确认数据移出顺序。
PRU写R31无法触发主机中断1. INTC事件映射未配置。
2. 主机Arm侧的中断控制器(如GIC)未使能对应中断线。
3. PRU写入R31的向量值超出0-15范围,或vec_valid位未同时置1。
1. 检查PRU-ICSS INTC的配置,确认PRU系统事件(16-31)已正确映射到输出主机中断的通道。
2. 在Linux驱动或裸机程序中,确认已申请并使能了对应的中断号。
3. PRU代码中,确保写入R31的值格式为 `(1 << 5)
访问某内存地址或寄存器失败1. 使用了错误的内存映射地址(本地/全局混淆)。
2. 访问了写保护区域且未解锁。
3. PRU正在运行,主机试图访问其IRAM。
1. 明确当前操作主体(主机 or PRU)和访问目标,选择正确的基地址。
2. 如需配置被保护的寄存器,严格遵循解锁-配置-锁定的流程。
3. 主机访问PRU IRAM必须在PRU停止状态下进行。

最后一点个人体会:PRU编程更像是传统的单片机或FPGA逻辑编程,需要你对硬件寄存器、时序和内存布局有清晰的认识。调试时,逻辑分析仪是你的最佳伙伴,它能直观地展示引脚上的波形、PRU的指令流以及内存访问行为。开始时,建议从最简单的直接GPIO控制或共享内存通信例程入手,逐步增加复杂度,理解每一层配置带来的影响。PRU-ICSS是一个强大的工具,一旦掌握,你就能在复杂的SoC中开辟出一块确定性的实时疆域。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/20 13:10:40

AI搜索可见度优化:从技术原理到工程实践

随着生成式AI搜索引擎&#xff08;如百度ERNIE、字节豆包、阿里通义千问&#xff09;的普及&#xff0c;品牌和内容的AI可见度&#xff08;AI Visibility&#xff09;已成为数字营销的新课题。本文从技术角度解析AI搜索如何引用内容&#xff0c;并给出可落地的优化方案。 AI搜…

作者头像 李华
网站建设 2026/7/20 13:10:39

洛雪音乐音源完全指南:如何免费获取全网高品质音乐

洛雪音乐音源完全指南&#xff1a;如何免费获取全网高品质音乐 【免费下载链接】lxmusic- lxmusic(洛雪音乐)全网最新最全音源 项目地址: https://gitcode.com/gh_mirrors/lx/lxmusic- 想要免费聆听酷我、酷狗、QQ音乐、网易云等平台的高品质音乐吗&#xff1f;洛雪音乐…

作者头像 李华
网站建设 2026/7/20 13:08:50

LFM2.5-ColBERT-350M-4bit安全部署指南:许可证与商业使用注意事项

LFM2.5-ColBERT-350M-4bit安全部署指南&#xff1a;许可证与商业使用注意事项 【免费下载链接】LFM2.5-ColBERT-350M-4bit 项目地址: https://ai.gitcode.com/hf_mirrors/mlx-community/LFM2.5-ColBERT-350M-4bit 想要在Apple Silicon设备上高效部署多语言检索模型吗&a…

作者头像 李华
网站建设 2026/7/20 13:08:11

C语言自增运算符++详解:从语法到未定义行为避坑指南

这次我们来看 C 语言中的自增运算符 。对于初学者来说&#xff0c;这个看似简单的符号&#xff0c;往往是理解 C 语言表达式求值顺序、副作用和程序行为的关键门槛。它不仅是让代码更简洁的语法糖&#xff0c;更是理解编译器如何工作、程序如何执行的一扇窗口。 很多人在学…

作者头像 李华
网站建设 2026/7/20 13:07:33

Ubuntu与Linux Mint对比:桌面Linux的纯净与商业化之争

1. 当Ubuntu开始偏离初心&#xff1a;桌面Linux的广告化困局十年前我第一次接触Ubuntu时&#xff0c;那个橙褐色的登录界面让我眼前一亮——一个完全免费的操作系统&#xff0c;预装了完整的办公套件和媒体工具&#xff0c;甚至还能直接播放MP3文件&#xff08;这在当时的Linux…

作者头像 李华
网站建设 2026/7/20 13:07:06

算法-回溯

通用模板 /* 回溯算法框架 */ void backtrack(State state, List<Choice> choices, List<State> res) {// 判断是否为解if (isSolution(state)) {// 记录解recordSolution(state, res);// 不再继续搜索return;}// 遍历所有选择for (Choice choice : choices) {// 剪…

作者头像 李华