1. 项目概述与核心价值在嵌入式系统开发尤其是汽车电子和工业控制这类对可靠性、实时性要求严苛的领域芯片的稳定运行绝非偶然而是源于对底层硬件资源的精细化管理。这其中电源、复位和时钟管理Power, Reset, and Clock Management, PRCM是构建系统稳定性的基石。它就像一座精密工厂的总控室负责为各个车间芯片内部模块提供稳定、合规的电力、有序的启动流程和精准的节拍信号。TI的16xx系列芯片作为广泛应用于上述领域的高性能处理器其PRCM模块的设计尤为复杂和强大但也因此带来了较高的配置门槛。很多开发者拿到芯片后面对动辄数百页的技术参考手册和数据手册往往感到无从下手。特别是手册中那些以表格和位域描述为主的寄存器说明虽然信息准确但缺乏场景化的解读和“为什么这么配置”的深层逻辑。这就导致开发过程容易陷入“照猫画虎”的困境从示例代码里抄一段配置系统能跑起来就万事大吉一旦遇到功耗异常、外设时钟不准、或者系统莫名复位的问题排查起来就异常困难因为根本不理解这些配置寄存器背后的工作原理。本文的目的就是充当这个“总控室”的操作手册解读员。我不会仅仅罗列寄存器字段而是结合我多年在汽车ECU开发中调试TI C2000、ARM Cortex-R系列芯片的经验深入解析16xx系列芯片PRCM模块中几个关键且具有代表性的控制寄存器。我们将从共享内存分配SHMEMBANKSEL、内置自测试时钟控制PBISTCLKCTL到主子系统复位与时钟管理MSS_RCM的核心寄存器逐一拆解其设计意图、配置方法以及在实际项目中可能遇到的“坑”。无论你是正在评估16xx系列芯片的架构师还是正在进行底层驱动开发的工程师相信这篇结合了手册理论与实战经验的解析都能帮助你更自信地驾驭这颗芯片构建出更稳健、更高效的嵌入式系统。2. 核心寄存器功能解析与设计逻辑在深入具体寄存器之前我们有必要先建立对TI 16xx系列芯片PRCM模块的整体认知。这个模块并非一个孤立的单元而是深度嵌入芯片系统架构与内存控制器、互联总线、安全机制等紧密耦合。它的核心任务可以概括为三点资源分配、状态控制和错误管理。我们接下来要分析的几个寄存器组正是围绕这三点展开。2.1 共享内存主控分配寄存器SHMEMBANKSELSHMEMBANKSEL3TO0和SHMEMBANKSEL7TO4这两个寄存器是理解16xx系列多核/多主控架构内存访问的关键。芯片内部通常有一块共享的SRAML3RAM多个主控设备如DSS、MSS的TCMA/TCMB、OCLA、BSS等都需要访问它。如果没有仲裁机制就会发生访问冲突导致数据损坏或系统挂起。这两个寄存器的设计非常直观它将共享内存划分为多个“Bank”例如Bank0-Bank7每个Bank可以独立地分配给某一个特定的主控设备独占访问。寄存器中的每个8位字段如BANK0、BANK1...BANK7就对应一个内存Bank写入特定的值如0x01代表DSS0x02代表MSS TCMA即完成分配。为什么这么设计这实际上是一种静态的、基于硬件的内存访问仲裁策略。相比于动态仲裁器它的优势在于确定性极高访问延迟可预测非常适合汽车和工业控制中对实时性要求极高的场景。例如你可以确保关键的控制算法跑在MSS的TCMA里访问其专属的Bank时绝不会被其他主控如数据采集模块DSS打断从而保证了最坏情况下的执行时间。配置时的核心考量数据流分析在分配前必须厘清各个主控之间的数据流。哪些主控之间有频繁的数据交换它们应该被分配到相邻或可高效互访的Bank吗实际上在16xx中Bank分配更多是隔离而非促进交互交互通常通过邮箱Mailbox或DMA。性能与隔离的权衡全部Bank都给一个主控如DSS能最大化其性能但会剥夺其他主控的直接访问权。通常的做法是为每个需要高性能、低延迟数据访问的主控分配至少一个专属Bank用于存放其核心数据和代码。SHMEMBANKSEL3TO0的BANK0-3仅支持分配给DSS L3RAM这暗示了低地址Bank可能具有某些特定优化或固定用途。错误配置的后果如果为一个Bank写入了一个无效的主控ID例如向BANK0写入0x02手册明确指出“memory is not used”。这意味着该Bank将被禁用任何主控都无法访问导致预期的内存容量丢失可能引发程序运行异常或内存分配失败。这种错误在系统初始化阶段很难被发现因为不会立即触发硬件错误但会在后续动态内存分配或数据存取时暴露。2.2 PBIST时钟控制寄存器PBISTCLKCTLPBISTProcessor Built-In Self-Test是芯片内部用于自测试的逻辑模块通常在芯片上电启动或进入特定安全状态时运行以检测制造缺陷或运行期故障。PBISTCLKCTL寄存器就是为这个模块提供独立的时钟控制。独立时钟域的必要性为什么PBIST需要一个独立的、可配置的时钟而不是直接使用系统主时钟主要原因有三测试覆盖率在不同的时钟频率下运行测试可以暴露与频率相关的时序缺陷。功耗与热管理在测试期间可以降低PBIST时钟频率以控制功耗和温升。与系统解耦PBIST测试可以在系统核心时钟保持静默或低速运行时进行避免相互干扰。寄存器字段精讲PBIST300MCLKSRCSEL(位7-4)时钟源选择。选项包括CPUCLK、RCCLK内部低速时钟、600MHz PLL分频时钟、以及来自模拟模块的REFCLK。关键点选择不同源意味着PBIST的时钟与系统不同部分的时钟树关联其稳定性和精度也不同。例如选择PLL分频时钟则PBIST时钟会随PLL锁定而稳定选择RCCLK则更简单但频率低。PBIST300MCLKDIV(位3-0)分频值。从1分频到15分频。这里有一个至关重要的操作顺序手册用Note强调“One Should change the divide value before switching to New clock.” 即必须先配置好分频值再切换时钟源。如果反过来在切换到一个新的、未知频率的时钟源的同时改变分频比可能会导致分频器产生毛刺或错误输出进而引起PBIST逻辑故障。PBIST300MCLKGATE(位15-8)时钟门控。这是一个“预分频器门控”即在时钟进入分频器之前就将其关闭。手册提到“to gate the clock either 3:0 should be 0xD or 7:4 should be 0xA”这是一种写保护机制。你不能简单地写1或0来开关时钟而必须写入一个特定的“魔法数字”Magic Number0xAD或高低半字节组合这有效防止了软件误操作导致时钟意外关断。2.3 主子系统复位与时钟管理寄存器MSS_RCMMSS_RCM是PRCM模块中面向主控制子系统Main Control Subsystem的配置集合功能非常繁杂从软复位、时钟分频源选择、内存初始化到错误注入与捕获一应俱全。我们挑几个最具代表性的来分析。2.3.1 软复位寄存器SOFTRST1/2SOFTRST1的CR4SYSRST字段和SOFTRST2的VIMRST字段提供了对特定模块CR4核心、VIM中断控制器进行“热复位”的能力。软复位与硬复位的区别在于硬复位会让整个芯片或子系统回到上电初始状态所有寄存器丢失而软复位通常只复位该模块的逻辑状态其配置寄存器可能保持不变或部分清除总线上的其他模块不受影响。这对于调试和错误恢复极其有用。例如当某个外设驱动卡死时可以尝试通过软复位该外设而不是重启整个CPU。操作关键同样需要写入特定值0xAD来触发。CR4SYSRST的描述中还提到“Self clearing”意味着该位在触发复位动作后会自动清零软件无需手动清除这简化了操作流程。2.3.2 时钟源选择与分频寄存器CLKSRCSEL0/1, CLKDIVCTL0这是时钟管理的核心。以CLKSRCSEL0为例它集中管理了RTI实时中断、QSPI串行闪存接口、FDCANCAN FD控制器等多个重要外设的时钟源选择。设计哲学为不同外设提供独立的时钟源选择允许开发者根据外设的实际需求和应用场景为其分配合适的时钟。例如FDCAN需要非常精确的时钟来保证通信波特率可能会选择来自外部晶振的REFCLK而QSPI在高速读写时可能选择PLL分频时钟以获得更高性能在低速待机时则切换到低功耗的RCCLK。CLKDIVCTL0则对应了分频控制。一个最佳实践是在改变某个外设的时钟配置时遵循“先门控、再改分频/源、最后解除门控”的顺序尤其是在切换时钟源时这样可以避免中间状态产生毛刺时钟。2.3.3 内存初始化与ECC控制寄存器MEMINITSTART/DONE, ECCENxxx在安全关键系统中内存上电后的状态是随机的直接使用可能导致不可预测的行为。MEMINITSTART寄存器允许软件主动触发对各个内存块如TCM、DMA RAM、VIM RAM、安全RAM以及各子系统间的邮箱内存的硬件初始化通常是将内存内容清零或写入固定值。操作流程先向MEMINITKEY字段写入0xAD使能初始化功能然后对需要初始化的内存对应的位写1。完成后查询对应的MEMINITDONE寄存器状态位确认初始化完成。ECC错误校正码对于用于安全通信的邮箱内存如MSS-GEM, BSS-GEMECCENMSSGEM和ECCENBSSGEM寄存器用于使能ECC功能。ECC能检测和纠正单位错误检测双位错误对于防止因宇宙射线或电磁干扰导致的软错误至关重要。使能同样需要写入0xAD。而ECCCAPTMSSGEM等寄存器则用于捕获发生ECC错误时的地址和修复位信息是进行故障诊断和系统健康管理的宝贵数据源。2.3.4 关键寄存器KEY与用户模式访问KEY寄存器偏移ACh是一个典型的“锁匙”寄存器。MSS_RCM中的许多控制寄存器关乎系统根本稳定性不允许被用户模式非特权模式下的代码随意修改。在尝试写入这些受保护的寄存器之前必须先向KEY寄存器写入特定的解锁密钥0x83E783E7h。写入后CPU才获得临时权限去修改其他RCM寄存器。这是一种重要的硬件级保护机制防止应用程序跑飞后意外篡改时钟、复位等关键设置导致系统崩溃。3. 实战配置流程与操作要点理解了寄存器原理后我们来看如何将这些知识应用到实际的系统初始化代码中。以下是一个典型的启动阶段PRCM配置流程我会穿插说明每个步骤的意图和注意事项。3.1 系统启动初期的PRCM配置顺序系统从上电复位释放后通常运行在Boot ROM中之后会跳转到用户应用程序。在应用程序的启动代码如main()之前的Startup.s或c_int00函数中我们需要对PRCM进行初步配置。一个基本原则是先稳定时钟再初始化内存最后配置外设时钟和复位。步骤一解锁写权限如果需要如果后续操作需要修改MSS_RCM中受保护的寄存器第一步就是解锁。// 假设 MSS_RCM 模块基地址为 0xFFFF F800 #define MSS_RCM_BASE (0xFFFFF800UL) #define MSS_RCM_KEY (*(volatile uint32_t *)(MSS_RCM_BASE 0x0ACU)) void PRCM_Init(void) { // 解锁 MSS_RCM 寄存器写权限 MSS_RCM_KEY 0x83E783E7U; // 注意解锁后后续对RCM的配置需尽快完成减少窗口期。 }步骤二配置共享内存Bank分配根据系统架构设计确定各主控的内存映射。例如我们希望Bank0-3给DSSBank4给MSS TCMA用于关键中断服务例程Bank5给OCLA用于跟踪调试。#define SHMEMBANKSEL3TO0 (*(volatile uint32_t *)(0xFFFFE2C8U)) // IWR 空间地址示例 #define SHMEMBANKSEL7TO4 (*(volatile uint32_t *)(0xFFFFE2CCU)) void Configure_Shared_Memory(void) { // Bank0-3: 分配给 DSS (0x01) // 每个Bank字段8位值0x01。所以BANK30x01, BANK20x01, BANK10x01, BANK00x01 // 组合成一个32位值0x01010101 SHMEMBANKSEL3TO0 0x01010101U; // Bank4: 分配给 MSS TCMA (0x02) // Bank5: 分配给 OCLA (0x08) // Bank6,7: 保留或分配给其他主控此处示例给MSS TCMA和BSS TCMA // BANK7(31-24)0x02, BANK6(23-16)0x10, BANK5(15-8)0x08, BANK4(7-0)0x02 // 组合值0x02100802 SHMEMBANKSEL7TO4 0x02100802U; }注意此配置通常在系统初始化早期、任何主控开始频繁访问共享内存之前完成且之后一般不动态修改属于静态配置。步骤三初始化关键内存在启用ECC或执行关键任务前初始化TCM、邮箱等内存。#define MSS_RCM_MEMINITSTART (*(volatile uint32_t *)(MSS_RCM_BASE 0x05CU)) #define MSS_RCM_MEMINITDONE (*(volatile uint32_t *)(MSS_RCM_BASE 0x06CU)) void Initialize_Memory(void) { uint32_t timeout 100000U; // 超时计数器 // 1. 使能内存初始化功能 // 向MEMINITKEY字段位31-24写入0xAD MSS_RCM_MEMINITSTART (0xADUL 24); // 2. 触发MSS TCMA和TCMB内存初始化 // 设置CR4TCMAMEM位0和CR4TCMBMEM位1为1 MSS_RCM_MEMINITSTART | (0x3UL); // 设置bit0和bit1 // 3. 轮询等待初始化完成 while (((MSS_RCM_MEMINITDONE 0x3UL) ! 0x3UL) (timeout 0)) { timeout--; } if (timeout 0) { // 内存初始化超时应进入错误处理流程 Error_Handler(); } // 4. 可选初始化其他内存如VIM, DMA等 // MSS_RCM_MEMINITSTART | (1UL 3); // 初始化VIMMEM }步骤四配置外设时钟以配置FDCAN和QSPI时钟为例假设我们需要FDCAN使用80MHz的REFCLK并2分频得到40MHz的工作时钟QSPI使用VCLK假设为200MHz4分频得到50MHz。#define MSS_RCM_CLKSRCSEL0 (*(volatile uint32_t *)(MSS_RCM_BASE 0x01CU)) #define MSS_RCM_CLKDIVCTL0 (*(volatile uint32_t *)(MSS_RCM_BASE 0x018U)) #define MSS_RCM_CLKGATE (*(volatile uint32_t *)(MSS_RCM_BASE 0x03CU)) void Configure_Peripheral_Clocks(void) { uint32_t regVal; // 1. 配置FDCAN时钟 // a) 先门控FDCAN时钟可选确保配置期间无时钟输出 MSS_RCM_CLKGATE | (1UL 5); // 设置FDCANCLKGATE位 // b) 设置分频值 (2分频 写入 1) regVal MSS_RCM_CLKDIVCTL0; regVal ~(0xFFUL 24); // 清除FDCANCLKDIV旧值 regVal | (1UL 24); // 设置FDCANCLKDIV 1 (代表2分频) MSS_RCM_CLKDIVCTL0 regVal; // c) 选择时钟源 (REFCLK from ANA, 编码为110) regVal MSS_RCM_CLKSRCSEL0; regVal ~(0xFUL 8); // 清除FDCANCLKSRCSEL旧值 regVal | (0x6UL 8); // 设置FDCANCLKSRCSEL 0x6 (REFCLK) MSS_RCM_CLKSRCSEL0 regVal; // d) 解除FDCAN时钟门控 MSS_RCM_CLKGATE ~(1UL 5); // 2. 配置QSPI时钟 (类似流程使用QSPICLKGATE, QSPICLKSRCSEL等位) // 注意QSPI的分频值在CLKDIVCTL1或其他寄存器需查手册确认。 // 此处为示例流程。 MSS_RCM_CLKGATE | (1UL 3); // 门控QSPI时钟 // ... 设置分频和时钟源 ... MSS_RCM_CLKGATE ~(1UL 3); // 解除门控 }3.2 动态功耗管理示例切换低功耗模式在汽车电子中ECU在不同运行状态如正常驾驶、怠速、休眠下功耗要求不同。PRCM允许动态调整时钟。void Enter_Low_Power_Mode(void) { // 假设进入低功耗模式前需要将部分外设时钟切换到低速的RCCLK // 1. 将FDCAN时钟切换到RCCLK (10MHz)并增加分频以进一步降速 MSS_RCM_CLKGATE | (1UL 5); // 门控 // 设置分频例如8分频 (写入7) uint32_t temp MSS_RCM_CLKDIVCTL0; temp ~(0xFFUL 24); temp | (7UL 24); MSS_RCM_CLKDIVCTL0 temp; // 切换时钟源到RCCLK (编码 001) temp MSS_RCM_CLKSRCSEL0; temp ~(0xFUL 8); temp | (0x1UL 8); MSS_RCM_CLKSRCSEL0 temp; MSS_RCM_CLKGATE ~(1UL 5); // 解除门控 // 2. 类似地调整其他外设时钟... // 3. 可能还需要调整MSS子系统时钟(VCLK)的源和分频 // 4. 最后可能通过写SOFTRST1/2或控制CR4的睡眠模式进入更深度的省电状态。 }4. 常见问题排查与调试技巧即使按照手册配置在实际项目中依然会遇到各种问题。以下是我在调试16xx系列芯片PRCM相关问题时总结的一些常见故障场景和排查思路。4.1 问题一系统启动后某个外设如QSPI无法正常工作读写失败。排查步骤检查时钟这是最常见的原因。首先确认该外设的时钟是否已使能对应CLKGATE寄存器的门控位是否为0。然后读取CURRCLKDIV0/1等寄存器确认当前实际的分频值是否与配置相符。最后用示波器或逻辑分析仪测量该外设的时钟引脚如果引出验证时钟频率和波形是否正确。检查复位状态确认外设是否处于复位状态。查看该外设自身的控制寄存器或者检查MSS_RCM中是否有针对它的软复位位被意外置位。有些外设模块还有独立的复位控制寄存器。检查内存映射与访问权限如果外设需要通过共享内存与主控通信确认对应的内存Bank是否已正确分配给该主控检查SHMEMBANKSEL。同时检查USERMODEEN和NSYSPERUSERMODEN寄存器确保当前CPU运行模式用户模式/特权模式拥有访问该外设寄存器的权限。检查电源域有些外设可能位于独立的电源域。确保该电源域已经上电且稳定。这可能需要检查PMIC电源管理芯片的配置或芯片的其他电源控制寄存器。4.2 问题二系统运行一段时间后发生ECC错误中断。排查步骤定位错误源读取ECCCAPTMSSGEM、ECCCAPTBSSGEM或SECURERAMECC寄存器。这些寄存器会捕获出错的内存地址ecc_fault_address和修复的位信息repaired_bit。分析地址将捕获的地址与你的内存映射表对比确定是哪个邮箱或哪块安全RAM发生了错误。是DSS-MSS邮箱还是MSS-BSS邮箱判断错误类型单比特错误会被ECC纠正但会记录双比特错误无法纠正通常会触发不可屏蔽中断NMI或错误信号。查看ESM错误信令模块的状态寄存器确定错误严重等级。采取行动单比特软错误可能是偶发的宇宙射线干扰。记录日志清除错误地址向ECCENxxx寄存器的对应清除位写111b系统可继续运行。但需监控发生频率若频繁发生需考虑硬件问题如电源噪声、内存质量问题。双比特错误或频繁单比特错误属于严重故障。应启动安全机制如切换到备份计算单元、进入安全状态如limp-home模式并记录故障码供后续分析。切勿简单地清除错误后继续运行。预防措施确保使能了关键内存的ECC功能正确写入0xAD到ECCENxxx寄存器。在系统初始化时对ECC保护的内存进行完整的读写测试March C/Algorithm等提前排除硬故障。4.3 问题三尝试修改时钟配置后系统锁死或行为异常。排查步骤违反操作顺序回顾是否违反了“先改分频后切时钟源”或“先门控后配置”的原则。特别是在切换时钟源时未先设置好分频器导致瞬间产生极高或极低频率的时钟使同步逻辑失效。时钟源不稳定切换到了一个尚未稳定或禁用的时钟源。例如在PLL未锁定时就切换到其分频时钟。确保目标时钟源已就绪通过检查PLL锁定状态位等。核心时钟被意外修改错误地修改了MSS子系统时钟VCLK的源或分频导致CPU本身运行频率突变程序跑飞。修改核心时钟必须极其谨慎通常需要在芯片设计允许的特定低功耗模式下进行并配合必要的指令缓存同步操作。使用调试器检查如果还能连接调试器第一时间检查RSTCAUSE复位原因寄存器。它能告诉你系统上次是为何复位的上电、看门狗、软件复位等。同时检查CLKINUSE寄存器查看当前各时钟源的实际使用状态与你的配置进行比对。4.4 调试技巧与工具使用心得寄存器地图与头文件务必使用TI官方提供的CMSIS-SVD文件或精心编写的寄存器定义头文件。确保每个寄存器的地址、位域偏移和掩码都是正确的。一个错误的位域定义可能导致调试数日无果。静态代码分析在编写PRCM配置代码时大量使用位操作和魔法数字。建议将关键配置值如0xAD,0x83E783E7以及各字段的偏移定义为有意义的宏并在关键配置函数前后添加详尽的注释说明此处配置的目的和约束条件。利用只读状态寄存器像CURRCLKDIV0/1、MEMINITDONE、RSTCAUSE这类只读寄存器是你的“眼睛”。在初始化函数中可以在关键步骤后读取并打印通过调试串口或ITM这些状态值验证配置是否生效。分阶段初始化不要试图在系统启动的最开始就完成所有PRCM配置。采用分阶段策略第一阶段只配置保证CPU最小化运行和调试接口如DAP/JTAG所需的时钟和电源第二阶段初始化内存和核心外设第三阶段再配置应用层的外设时钟。这样即使后期配置出错你仍然有机会通过调试器连接并查看第一阶段的状态。文档版本管理TI的芯片参考手册TRM和数据手册Datasheet会更新。务必确认你使用的寄存器描述与芯片的硅版本Silicon Revision相匹配。不同修订版的芯片某些寄存器的复位值或行为可能有细微差别这些差别往往在勘误表Errata中列出。5. 高级主题安全性与功能安全考量在ISO 26262或IEC 61508等功能安全标准下PRCM的配置不再仅仅是功能实现更是安全机制的一部分。1. 时钟监控Clock Monitoring虽然本文提及的寄存器未直接显示外部时钟监控逻辑但在安全相关的设计中通常会启用时钟丢失检测Clock Loss Detection或时钟质量监控。需要配置相关模块可能是ANA或专用的安全模块来监控关键时钟源如外部晶振、PLL输出一旦发现故障能触发ESM错误信令模块进而产生中断或复位确保系统进入安全状态。ESMGATE0-4寄存器就用于控制哪些错误线可以触发ESM需要根据安全分析报告进行配置。2. 寄存器保护与冗余KEY寄存器是基础保护。更进一步对于安全关键配置如复位源使能、看门狗时钟源应考虑采用时间冗余或逻辑冗余写入。例如连续两次写入相同的配置值中间插入读回验证或者由两个独立的软件分区如ASIL-B和ASIL-QM协同完成关键配置一个负责写另一个负责校验。3. ECC的初始化与测试安全标准要求对ECC等安全机制进行上电自检Power-On Self-Test, POST。在使能ECC后应主动向受保护的内存写入并读取已知模式的数据验证ECC的检错纠错功能是否正常。这可以通过在启动阶段调用专门的测试函数来实现。4. 复位网络管理SOFTRST1/2提供了细粒度的复位控制。在安全系统中需要清晰定义每种复位上电复位、看门狗复位、软件复位、VIM复位的触发条件、作用范围和系统恢复流程。例如一个非关键外设的故障可能仅触发其自身软复位而核心通信总线错误则可能需触发MSS子系统复位。这些策略需要在系统安全需求层面进行设计并通过配置PRCM及其他相关模块来实现。5. 配置数据的完整性校验PRCM的配置值本质上构成了系统运行的基础环境。在应用程序中可以定期或在进入关键操作前读回关键的PRCM寄存器如时钟源选择、分频比与预期的安全配置进行比较。如果发现被意外修改例如因软件跑飞或硬件瞬态故障应立即触发安全响应。这需要将预期的安全配置表保存在受保护的内存如带ECC的TCM中。通过对TI 16xx系列芯片PRCM寄存器的深入剖析我们可以看到一个稳健的嵌入式系统底层是无数个精心设计的控制位在协同工作。从内存资源的静态划分到时钟树的动态调整再到错误状态的实时捕获每一个细节都影响着系统的性能、功耗和可靠性。掌握这些寄存器的原理和配置技巧意味着你不仅能让芯片“跑起来”更能让它按照你设计的节奏安全、稳定、高效地运行。在调试中遇到问题时这份对底层硬件的理解将成为你定位问题根源最有力的工具。记住数据手册是你的地图而对这些控制寄存器的深刻理解则是你的指南针。