
1. 项目概述从芯片手册到实战设计在嵌入式系统开发尤其是基于德州仪器TITMS320C55x系列DSP的项目中拿到一份动辄数百页的芯片手册Datasheet是常态。手册里那些密密麻麻的表格、时序图和寄存器地址对于新手来说常常是“每个字都认识连起来就不知道在说什么”。今天我就以手头一个老项目——基于TMS320VC5505的工业数据采集板——为蓝本来聊聊我是如何从手册里那些冰冷的参数比如tr(D)4ns,VCRS1.3-2V出发一步步完成USB通信、GPIO控制和系统监控设计的。这不仅仅是读手册更是理解如何让一个DSP芯片在真实的电路板上“活”起来稳定可靠地工作。TMS320VC5505作为一款经典的定点DSP其外设的丰富性和可配置性是其强大功能的基础。但手册往往只告诉你“是什么”What而项目实战需要我们解决“怎么用”How和“为什么这么用”Why。本文将聚焦于三个最常用也最核心的外设模块USB2.0、通用定时器/看门狗GP Timer/Watchdog以及通用输入输出GPIO和JTAG调试接口。我会结合具体的电气参数、寄存器操作和实际调试中踩过的坑为你还原一个从理论到实践的完整设计过程。无论你是正在评估VC5505还是已经深陷调试泥潭希望这些经验能给你带来一些实实在在的帮助。2. USB2.0接口从电气参数到稳定通信USB接口是VC5505与上位机如PC进行高速数据交换的主要通道。手册第6.17.2节的电气数据/时序表是硬件设计和软件驱动的基石绝不能只看个大概。2.1 关键电气参数解读与硬件设计要点表6-44里的参数每一个都关系到信号完整性。我们逐条拆解1. 上升/下降时间tr(D), tf(D)全速模式12 Mbps要求4ns ≤ tr/tf ≤ 20ns。这个范围比较宽常规的PCB走线控制50欧姆阻抗和USB PHY芯片驱动都能轻松满足。但要注意这个参数是在CL50pF的测试负载下测得的。在实际设计中你需要估算连接器、走线和ESD保护器件带来的总负载电容确保不会超标。我通常会在USB_DP/DM线上串联一个22欧姆的小电阻靠近DSP端它既能轻微减缓边沿、改善信号质量也能起到一定的阻抗匹配和限流作用。高速模式480 Mbps要求tr/tf ≤ 0.5ns。这是一个非常苛刻的要求意味着信号边沿必须极其陡峭。仅靠VC5505集成的收发器Transceiver和普通的PCB设计很难独立满足。此时必须严格遵循高速信号布线规则阻抗控制DP/DM差分线必须做90欧姆±10%的差分阻抗控制。这需要与PCB板厂紧密沟通提供准确的叠层结构、线宽和线距。等长处理差分对内的两条走线长度差要控制在5mil约0.127mm以内以减少共模噪声和信号畸变。参考平面DP/DM走线下方必须有完整、不间断的地平面作为参考避免跨分割。ESD保护器件必须选用低电容通常要求0.5pF的专用USB ESD保护芯片例如Semtech的RClamp0524P。普通的TVS管电容太大会严重劣化高速信号。2. 边沿时间匹配trfM参数trfM定义为(tr/tf) * 100%要求在全速模式下保持在90%到111%之间。这意味着上升时间和下降时间要尽可能对称。不对称的边沿会导致信号占空比失真在接收端引入时序抖动。在硬件上确保DP/DM走线对称、负载平衡是满足此要求的关键。在软件上虽然无法直接调整但如果你发现全速通信误码率高可以检查PCB设计是否存在不对称因素。3. 交叉点电压VCRSVCRS是指USB差分信号在切换时DP和DM线交叉点的电压范围是1.3V到2.0V。这个电压由DSP内部的收发器电路决定通常我们无法直接调节。它的主要意义在于诊断如果你用示波器测量差分信号发现交叉点电压远低于1.3V或高于2.0V很可能意味着驱动能力不足、负载过重或电源有问题。这是一个判断硬件是否健康的有效指标。4. 驱动器输出阻抗ZDRV与接收器输入阻抗ZINPZDRV为40.5至49.5欧姆。这是一个非常关键的参数它要求我们在PCB设计时将DP/DM的走线特性阻抗设计为45欧姆左右通常标准是90欧姆差分阻抗即单端45欧姆。阻抗不匹配会导致信号反射在高速模式下表现为眼图闭合通信失败。ZINP≥ 100k欧姆。接收端的高输入阻抗是为了最小化对总线信号的影响。我们在设计时要避免在DP/DM线上并联额外的、低阻抗的负载。实操心得眼图测试是金标准对于高速USB设计千万不要以为画完PCB、程序能枚举就万事大吉。一定要用高速示波器配合USB眼图测试夹具进行眼图测试。合规的眼图信号幅度、抖动、交叉点电压等都在“眼睛”张开区域内是通信长期稳定的唯一保证。我们曾有一个批次板子常温下工作正常但高温下USB频繁掉线最后用眼图发现高温下信号抖动超标根源是时钟源温漂过大。2.2 软件驱动配置与枚举流程硬件是基础软件则是让USB工作的灵魂。VC5505的USB模块集成度较高但配置步骤仍需严谨。1. 时钟与电源配置USB模块需要48MHz的时钟。这个时钟通常由外部晶振提供并通过DSP内部的PLL和时钟分频器产生。首先要确保系统时钟配置正确并开启USB模块的时钟门控通常在外设时钟控制寄存器中。同时要检查并确保USB模块的模拟电源AVDD_USB和数字电源DVDD_USB稳定、干净。模拟电源的纹波要特别小建议使用LDO单独供电并搭配π型滤波电路。2. 引脚复用配置VC5505的USB_DP/DM引脚可能与其他功能复用。必须通过设备配置寄存器例如PINMUX或PERCFG寄存器将其明确设置为USB功能。这个步骤非常容易遗漏导致USB引脚“沉默”无法被主机检测到。3. 寄存器初始化与描述符设置USB协议栈的初始化较为复杂TI通常会提供库函数如USB Stack库。核心步骤包括设置USB控制寄存器USBCTL选择工作模式全速/高速。配置端点Endpoint相关寄存器包括端点类型控制、中断、批量、同步、最大包长度、缓冲区地址等。在程序中定义并存储完整的USB设备描述符、配置描述符、接口描述符、端点描述符和字符串描述符。这些描述符告诉主机“我是什么设备、有什么能力”。4. 中断服务程序ISR编写USB通信是事件驱动的严重依赖中断。你需要编写USB中断服务程序并根据中断状态寄存器的标志位来处理诸如总线复位、挂起、唤醒、传输完成等事件。特别是对于批量传输Bulk Transfer和中断传输Interrupt Transfer需要在对应端点的传输完成中断中及时处理数据缓冲区。// 示例简化的USB中断服务程序框架 interrupt void USB_ISR(void) { unsigned int usbIntStatus USB_GET_INT_STATUS(); // 读取中断状态 if (usbIntStatus USB_INT_RESET) { // 处理总线复位事件重新初始化端点状态 handleBusReset(); USB_CLEAR_INT_FLAG(USB_INT_RESET); } if (usbIntStatus USB_INT_EP2_TX) { // 假设端点2为批量输出端点 // 处理从主机接收到的数据 processReceivedData(EP2_BUFFER); // 重新使能端点接收准备下一次传输 USB_ENABLE_EP_RX(EP2); USB_CLEAR_INT_FLAG(USB_INT_EP2_TX); } if (usbIntStatus USB_INT_EP3_TX) { // 假设端点3为批量输入端点 // 数据已成功发送给主机可以准备下一包数据 prepareNextDataPacket(EP3_BUFFER); USB_CLEAR_INT_FLAG(USB_INT_EP3_TX); } // ... 处理其他中断标志 }5. 枚举与通信上电并正确初始化后主机PC会检测到USB设备插入并开始枚举过程请求描述符、分配地址、设置配置。你的固件必须能正确响应这些标准请求。枚举成功后便可根据自定义的协议通过批量传输端点进行应用层的数据收发。3. 通用定时器与看门狗系统的“心跳”与“保镖”VC5505提供了三个32位通用定时器其中Timer2可配置为看门狗。它们是实现精准定时、任务调度和系统抗干扰的利器。3.1 通用定时器原理与配置详解每个定时器的核心是一个32位递减计数器。它以一个经过预分频的时钟为基准进行计数减到0时产生中断并可选择自动重载周期值重新开始。1. 时钟源与预分频定时器的时钟源是CPU的系统时钟SYSCLK。通过一个13位的预分频器Prescaler进行分频。分频值由定时器控制寄存器TCR中的TDDRTimer Divide-Down Register字段和预分频计数器PSC共同决定。实际驱动递减计数器的时钟频率为定时器时钟 SYSCLK / (TDDR 1)。预分频器的作用是将高速的系统时钟降到适合定时任务的频率。例如系统时钟100MHz若想产生1ms的定时直接对100MHz计数需要100,000次而通过预分频将其降到1MHz后只需计数1000次更节省计数器位数。2. 周期寄存器与计数寄存器周期寄存器TIMPRD这是一个32位寄存器由TIMPRD1和TIMPRD2两个16位寄存器组成用于设置定时器的周期。当递减计数器减到0后若处于自动重载模式会将这个值重新加载到计数寄存器。计数寄存器TIMCNT同样为32位TIMCNT1和TIMCNT2它存储着当前的计数值并随着时钟递减。3. 工作模式与控制逻辑通过TCR寄存器控制FUNC位选择定时器模式00停止/保持0132位递减计数。CP位选择时钟源0内部时钟1外部时钟。PWID位脉冲宽度在特定模式下使用。ARB位自动重载使能。这是关键当ARB1时计数器在减到0并产生中断后会自动将TIMPRD的值加载到TIMCNT从而实现周期性中断无需软件干预。TSS位定时器开始/停止控制0启动1停止。4. 定时中断周期计算假设系统时钟SYSCLK 100MHz (周期C10ns)预分频值TDDR 9周期寄存器值TIMPRD 9999。定时器输入时钟周期 (TDDR 1) * C (91)*10ns 100ns即频率为10MHz。定时中断周期 (TIMPRD 1) * 定时器时钟周期 (99991)*100ns 1,000,000ns 1ms。 这样我们就配置了一个1ms的周期性定时器中断。// 示例配置Timer0产生1ms中断 void Init_Timer0(void) { // 停止定时器 *TCR0 | 0x0001; // 设置TSS1停止 // 设置预分频器 (TDDR9, PSC初始值9) *TCR0 ~(0xF000); // 清除TDDR位域 *TCR0 | (9 12); // 设置TDDR9 // 注意PSC值在定时器启动时会从TDDR加载通常无需单独设置 // 设置周期值 9999 (0x270F) *TIMPRD10 0x270F; // 低16位 *TIMPRD20 0x0000; // 高16位 // 设置自动重载和功能模式 *TCR0 ~0x0002; // 清除FUNC位选择32位递减计数模式假设 *TCR0 | 0x0040; // 设置ARB1使能自动重载 // 清除可能存在的定时器中断标志在IFR或定时器独立标志寄存器中 // 使能定时器中断在IER或定时器控制寄存器中 // 启动定时器 *TCR0 ~0x0001; // 清除TSS0启动 }3.2 看门狗定时器系统故障的最后防线看门狗Watchdog是嵌入式系统的“生命保险”。其核心思想是在正常运行的软件中定期“喂狗”重置看门狗计数器如果软件跑飞或陷入死循环无法按时喂狗看门狗计数器溢出则产生系统复位信号强制DSP重启使系统恢复到一个已知的初始状态。VC5505的Timer2可以配置为看门狗模式。与通用定时器不同看门狗有额外的保护机制锁寄存器防止软件意外修改其配置或提前喂狗。1. 看门狗关键寄存器解析看门狗使能锁存寄存器WDENLOK与使能寄存器WDEN这是一个“解锁-写入”机制。要向WDEN寄存器写入使能值0x55AA或0xAA55具体值查手册必须先向WDENLOK写入正确的解锁序列通常是0xAAAA和0x5555。这个设计防止了程序指针跑飞后意外禁用看门狗。看门狗踢狗锁存寄存器WDKCKLK与踢狗寄存器WDKICK喂狗操作同样需要解锁。必须先向WDKCKLK写入解锁序列然后向WDKICK写入特定的踢狗值如0xAAAA和0x5555。这确保了只有设计好的喂狗函数能正确喂狗随机指令无法误操作。看门狗预分频锁存WDPSLR与预分频寄存器WDPS设置看门狗时钟分频决定计数速度。看门狗起始值锁存WDSVLR与起始值寄存器WDSVR设置看门狗计数器的初始值。这是一个16位递减计数器。2. 看门狗超时时间计算超时时间T_wd由以下公式决定T_wd (WDSVR 1) * (WDPS 1) * T_sysclk其中T_sysclk是系统时钟周期。例如SYSCLK100MHz,WDPS1023,WDSVR65535则超时时间约为(65536)*(1024)*10ns ≈ 0.67秒。你需要根据系统中最长的关键任务执行时间来合理设置这个值既要给正常任务留足时间又要在故障时能及时复位。3. 看门狗配置与喂狗流程// 示例看门狗初始化与喂狗函数 void Init_Watchdog(void) { // 1. 解锁并设置预分频器 *WDPSLR 0xAAAA; *WDPSLR 0x5555; *WDPS 1023; // 设置预分频值 // 2. 解锁并设置起始值 *WDSVLR 0xAAAA; *WDSVLR 0x5555; *WDSVR 65535; // 设置计数器起始值 // 3. 解锁并使能看门狗 *WDENLOK 0xAAAA; *WDENLOK 0x5555; *WDEN 0xAA55; // 写入使能值请根据具体手册确认 // 4. 解锁序列初始化踢狗锁存可选通常在喂狗函数中做 // *WDKCKLK 0xAAAA; // *WDKCKLK 0x5555; } void Feed_Watchdog(void) { // 喂狗操作必须严格按顺序进行 *WDKCKLK 0xAAAA; *WDKCKLK 0x5555; *WDKICK 0xAAAA; *WDKICK 0x5555; // 写入特定的踢狗值序列 }注意事项喂狗策略喂狗函数Feed_Watchdog()必须放在系统主循环或一个确保能定期执行的定时器中断中。绝对不要在有阻塞风险的代码段如长时间循环等待某个外部事件中喂狗否则一旦外部事件不出现看门狗就会超时复位即使程序逻辑本身没跑飞。正确的做法是采用状态机等非阻塞编程模型确保主循环始终能周期性执行。4. GPIO模块灵活的数字接口与中断控制GPIO是连接DSP与外部数字世界按键、LED、传感器、继电器等的桥梁。VC5505的GPIO功能强大支持方向控制、数据读写和边沿中断。4.1 GPIO寄存器详解与基本操作GPIO功能通过一组内存映射寄存器控制地址范围0x1C06h - 0x1C11h。这些寄存器是16位的但每个GPIO引脚对应一个比特位。1. 方向寄存器IODIR1,IODIR2用于设置每个引脚是输入0还是输出1。上电复位后所有GPIO默认为输入模式内部下拉这是一个安全的状态防止在配置完成前引脚意外输出驱动外部电路。2. 数据输出寄存器IODATAOUT1,IODATAOUT2当引脚配置为输出时向该寄存器的对应位写1或0即可控制引脚输出高电平或低电平。读取该寄存器返回的是当前锁存的输出值而非引脚的实际物理电平。3. 数据输入寄存器IOINDATA1,IOINDATA2无论引脚配置为输入还是输出读取该寄存器都能获得引脚当前的实际物理电平。这是检测外部信号状态的方式。4. 中断相关寄存器这是GPIO的高级功能允许GPIO引脚在检测到电平边沿时向CPU发起中断。中断边沿触发使能寄存器IOINTEDG1,IOINTEDG2设置每个引脚的中断触发方式。0 下降沿触发1 上升沿触发。中断使能寄存器IOINTEN1,IOINTEN2全局开关控制每个引脚的中断请求是否被允许提交给CPU。中断标志寄存器IOINTFLG1,IOINTFLG2当使能的引脚上发生了设定的边沿事件时对应的标志位会被硬件置1。在中断服务程序中必须通过向该位写1来清除标志否则会持续产生中断。4.2 输入/输出时序分析与设计考量手册中的表6-50和表6-51给出了GPIO的时序参数理解它们对设计可靠的外部电路至关重要。1. 输入时序要求tw(ACTIVE)输入有效脉冲的最小宽度。要求至少为2CC为系统时钟周期。例如100MHz系统下C10ns脉冲宽度需≥20ns。这决定了外部设备如按键、传感器产生的信号必须保持至少20ns的有效电平才能被GPIO输入逻辑可靠地锁存。对于机械按键这完全不是问题但对于高速数字信号就需要检查是否满足。tw(INACTIVE)输入无效脉冲的最小宽度。要求至少为1C10ns。2. 输出时序特性tw(GPOH)/tw(GPOL)GPIO输出高/低电平的最小脉冲宽度。手册给出的是3C30ns。注意这不是最大速度限制而是基于内部总线访问周期的保守值。它意味着如果你通过软件连续翻转同一个GPIO引脚其最高频率会受到内核访问GPIO寄存器速度的限制远低于这个参数值。对于生成精确的波形如PWM更推荐使用定时器或PWM外设。3. 输入延迟Latency表6-52的tL(GPI)参数非常重要它定义了从GPIO引脚事件发生到CPU能够感知到的延迟。轮询IOINDATA寄存器延迟≥5个时钟周期。这是软件不断读取输入寄存器的方式。轮询IOINTFLG寄存器延迟≥7个时钟周期。这种方式是检查中断标志位但未开启CPU中断。中断检测延迟≥8个时钟周期。这是配置好中断后由硬件自动触发CPU中断的延迟。这个延迟决定了系统对外部事件的响应速度。如果你需要检测一个非常短的脉冲接近tw(ACTIVE)下限或者要求极快的响应就必须考虑这个延迟。对于大多数应用如按键检测脉冲宽度在ms级以上这个延迟可以忽略不计。4.3 中断模式配置与防抖实践将GPIO配置为中断模式是实现高效事件驱动编程的关键。// 示例配置GPIO5假设为下降沿中断并设置中断服务程序 void Init_GPIO_Interrupt(void) { // 1. 配置GPIO5为输入模式 (假设GPIO5属于IODIR1寄存器第5位) *IODIR1 ~(1 5); // 2. 配置为下降沿触发 (IOINTEDG1第5位清0) *IOINTEDG1 ~(1 5); // 3. 使能GPIO5的中断 (IOINTEN1第5位置1) *IOINTEN1 | (1 5); // 4. 清除可能已存在的中断标志写1清0 *IOINTFLG1 | (1 5); // 5. 在CPU级别使能GPIO中断需要配置中断控制器如IER寄存器 // 假设GPIO中断对应CPU中断INT4 enable_CPU_interrupt(INT4); } // GPIO中断服务程序 interrupt void GPIO_ISR(void) { // 1. 判断是否是GPIO5的中断标志 if (*IOINTFLG1 (1 5)) { // 2. 处理中断事件例如读取引脚状态或设置事件标志 handle_GPIO5_event(); // 3. 非常重要清除中断标志位写1清0 *IOINTFLG1 | (1 5); } // ... 检查其他GPIO中断标志 }实操心得GPIO中断与软件防抖对于连接机械开关如按键的GPIO中断必须进行软件防抖。机械触点在闭合或断开时会产生毫秒级的抖动会产生多次边沿导致一次按键触发多次中断。一个简单有效的防抖方法是在中断服务程序中不立即处理动作而是设置一个标志并启动一个10-20ms的定时器延时。在定时器中断中再去读取GPIO状态如果状态稳定再执行真正的按键处理逻辑。这能极大提升系统的可靠性。5. JTAG接口调试与测试的生命线JTAGIEEE 1149.1标准接口是开发阶段不可或缺的用于程序下载、在线调试和边界扫描测试。5.1 关键信号与连接要点VC5505的JTAG接口包含以下信号TCK测试时钟输入。由仿真器提供。TMS测试模式选择。状态机控制信号。TDI测试数据输入。TDO测试数据输出。TRST测试复位低电平有效。这是最容易出问题的地方。手册特别强调了TRST引脚的处理芯片内部已有下拉电阻IPD确保上电时TRST为低有效从而正确初始化JTAG逻辑。在设计外部电路时必须确保TRST引脚在上电期间和正常工作时保持低电平。通常的做法是直接在TRST引脚到地之间连接一个4.7kΩ - 10kΩ的外部下拉电阻提供确定的低电平。当使用TI的XDS系列仿真器时仿真器会主动驱动TRST为高来释放复位。但如果你使用某些第三方仿真器它们可能期望TRST引脚外部有上拉电阻。在这种情况下绝对不能直接加上拉电阻否则会和内部下拉冲突导致上电状态不确定。正确的做法是使用一个跳线或0欧姆电阻在需要连接该仿真器时临时将外部下拉电阻断开或替换为更复杂的电平转换电路。最稳妥的方案永远是遵循TI的建议使用外部下拉电阻。5.2 时序参数与PCB布局要求表6-55和表6-56给出了JTAG端口的时序要求。虽然JTAG频率通常不高TCK周期tc(TCK) ≥ 60ns即频率≤16.7MHz但为了确保调试稳定尤其是长电缆连接时仍需注意tsu(TDIV-TCKH)和th(TCKH-TDIV)这是TDI/TMS相对于TCK上升沿的建立时间和保持时间分别为10ns和4ns。这意味着仿真器必须在TCK上升沿前至少10ns改变TDI/TMS数据并在之后保持至少4ns。td(TCKL-TDOV)这是TCK下降沿后TDO有效的最大延迟为28ns。仿真器需要在这个时间之后采样TDO数据。在PCB布局上TCK信号建议串联一个22-33欧姆的小电阻可以阻尼反射改善信号质量。JTAG信号线应尽可能短并远离晶振、开关电源等噪声源。如果仿真器电缆较长15cm应考虑在信号线上增加适当的端接。5.3 边界扫描与JTAG ID除了调试JTAG的另一个重要功能是边界扫描Boundary-Scan可用于生产测试检查PCB上芯片的焊接连通性。通过JTAG指令可以访问芯片的边界扫描链。手册中提到的JTAGID寄存器只读值为0x0009702F是仿真器/调试软件用来识别芯片型号的关键。当你连接仿真器时软件会读取这个ID。如果读到的ID不正确或全为0/全为F通常意味着JTAG连接有问题如TRST电平不对、TCK无时钟、TDO线断开等需要从硬件连接开始排查。6. 外设协同应用与系统设计经验在实际项目中这些外设很少孤立工作。例如一个典型的数据采集系统可能是这样的GPIO中断响应外部触发信号定时器精确控制ADC采样间隔采集到的数据通过USB批量传输实时上传到PC同时看门狗在后台默默守护防止程序死锁。1. 中断优先级与冲突管理VC5505的中断源很多USB、定时器、GPIO等。需要合理规划中断优先级通过中断选择寄存器配置。原则是对实时性要求高的、处理时间短的中断赋予更高的优先级。例如用于高速数据流控制的DMA中断优先级应高于按键扫描的GPIO中断。同时在中断服务程序中代码要尽量精简避免长时间关中断防止丢失其他重要事件。2. 低功耗设计中的外设管理在电池供电设备中功耗至关重要。VC5505的外设模块通常都有独立的时钟门控。当一个外设如USB、暂时不用的定时器不工作时应通过外设时钟控制寄存器关闭其时钟输入以节省动态功耗。对于GPIO未使用的引脚应配置为输出并置为低电平或配置为输入并使能内部上拉/下拉避免引脚悬空引起漏电流和振荡。3. 寄存器配置的原子性在配置外设寄存器尤其是控制寄存器时要注意操作的原子性。例如在修改定时器的TCR寄存器以改变其工作模式时如果先读后改再写read-modify-write在“读”和“写”之间如果发生中断并且中断服务程序也修改了同一个寄存器就会造成配置错误。对于关键配置可以考虑暂时关闭中断或者使用位域操作指令如果CPU支持来确保原子性。4. 硬件连接检查清单在调试任何外设不工作的故障时首先进行硬件检查电源用万用表和示波器检查该外设模块的电源引脚电压是否稳定、纹波是否在范围内。时钟该外设所需的时钟是否使能用示波器测量相关时钟引脚是否有波形。复位外设的软件复位位是否已释放硬件复位信号是否正常引脚复用这个外设的功能是否已映射到正确的物理引脚上检查PINMUX相关寄存器。连接信号线是否连通对高速信号如USB检查阻抗和眼图。从手册参数到稳定运行的系统中间隔着硬件设计、软件驱动和调试排错三大关。理解每个参数背后的物理意义掌握每个寄存器的正确操作顺序养成严谨的编程和调试习惯是驾驭像TMS320VC5505这样复杂DSP的必经之路。希望这篇基于实战的梳理能帮你把手册上的表格和数字变成手中稳定可靠的产品。