MPC823 SCC HDLC模式编程实战:从协议原理到嵌入式通信实现

发布时间:2026/6/14 13:14:13

MPC823 SCC HDLC模式编程实战:从协议原理到嵌入式通信实现 1. 项目概述与HDLC协议核心在嵌入式通信系统开发中数据链路层的可靠性与效率直接决定了整个系统的稳定性和性能。Motorola后为Freescale/NXP的MPC823处理器其内置的通信处理器模块CPM和串行通信控制器SCC是处理这类任务的利器。今天我们不谈泛泛的理论而是聚焦于SCC最经典、应用最广泛的模式之一HDLC模式。如果你正在开发基于MPC823的协议转换器、工业网关、或任何需要可靠串行数据链路的设备那么对SCC HDLC模式的深入理解和熟练编程将是绕不开的核心技能。HDLC协议作为ISO七层模型中数据链路层的基石其设计思想深刻影响了后续众多协议如SDLC、LAPB、LAPD乃至帧中继。它的核心魅力在于其简洁而强大的同步帧结构以及通过“零比特插入/删除”俗称位填充来保证帧定界标志0x7E在数据字段中的唯一性。这意味着对于上层交付的任意数据包HDLC控制器都能将其封装成标准的帧格式进行传输接收方则能准确地从比特流中识别出帧的开始与结束并恢复原始数据。MPC823的SCC在硬件层面完整实现了这一过程包括帧的组装/解析、CRC校验的生成与验证、地址匹配等从而将CPU从繁琐的比特级操作中解放出来专注于更高层的协议逻辑。2. MPC823 SCC HDLC模式架构与核心机制解析2.1 整体工作流程与核心思想MPC823的SCC模块在HDLC模式下其核心设计思想是“描述符驱动”和“事件驱动”。CPU并不直接参与每一个字节的收发而是通过预先设置好的一系列数据结构主要是缓冲区描述符和参数RAM来告诉SCC“数据在这里格式是这样处理完通知我”。SCC则作为独立的DMA控制器自动完成从内存读取数据、按HDLC格式封装发送或者从线路上接收数据、解析后存入内存的全过程。这种架构带来了几个显著优势。首先极低的CPU占用率。一旦初始化完成并启动收发SCC硬件与核心CPU异步工作仅在帧收发完成、缓冲区满/空或发生错误时才通过中断通知CPU进行后续处理。其次极高的灵活性。通过缓冲区描述符链表可以轻松实现多缓冲区拼接成一个帧或者一个长帧分散在多个缓冲区这为处理变长数据包提供了便利。最后强大的容错与诊断能力。硬件集成了多种错误计数器CRC错误、帧过长、地址不匹配等和详细的状态报告使得链路质量监控和故障排查变得直观。2.2 关键硬件模块与协作关系理解SCC HDLC编程首先要厘清几个关键硬件模块及其协作关系SCC内核负责HDLC协议的编解码包括位填充/删除、CRC计算、标志/空闲序列的生成与检测。参数RAM (Parameter RAM)这是一块位于CPM内部双端口RAM的特定区域每个SCC通道都有自己的一块。它存储了HDLC模式特有的配置参数如CRC常数、最大帧长、地址匹配寄存器、接收帧阈值等。这是SCC HDLC控制器的“配置中心”。缓冲区描述符 (Buffer Descriptor, BD)这是连接CPU内存数据与SCC硬件的桥梁。每个BD包含一个状态/控制字、数据长度字段和一个指向实际数据缓冲区的指针。发送和接收各有独立的BD表链表SCC通过遍历这些表来自动存取数据。SCC模式寄存器 (PSMR)定义了HDLC模式下的具体行为如CRC位数、帧间标志数量、是否启用自动重传等。SCC事件/状态/掩码寄存器 (SCCE, SCCS, SCCM)用于报告和使能各种事件中断如发送完成、接收完成、各种错误等。它们的工作流程可以概括为CPU初始化参数RAM和BD表 - 配置GSMR和PSMR寄存器使能SCC HDLC模式 - SCC根据BD表自动进行数据收发 - 收发完成后更新BD状态并触发中断如果使能- CPU在中断服务程序中处理数据并重置BD以供下次使用。注意参数RAM和BD表必须位于CPM可以访问的内存空间。通常它们被分配在片内双端口RAM中以确保最高的访问效率和确定的时序。如果放在外部SDRAM需确保内存控制器已正确初始化且访问不会成为瓶颈。3. 核心数据结构详解参数RAM与缓冲区描述符3.1 HDLC参数RAM内存映射与配置要点当SCC被配置为HDLC模式时其参数RAM区域会被覆盖为HDLC专用的数据结构。手册中的Table 16-27是编程的“地图”。我们不需要记住每一个地址但必须理解关键参数的作用。必须由程序员初始化的关键参数手册中以粗体标出C_MASK (CRC常量)与C_PRES (CRC预设值)这两个寄存器决定了CRC校验的算法。对于最常用的16位CCITT-CRC多项式X^16 X^12 X^5 1必须分别初始化为0x0000F0B8和0x0000FFFF。如果使用32位CRC则值不同。配置错误将导致收发双方CRC校验永远无法通过这是最常见的通信失败原因之一。MFLR (最大帧长寄存器)这是一个安全阀。SCC会将接收到的帧长度与此寄存器值比较。如果超过SCC会丢弃超长部分并在最后一个接收BD中设置LG长度违规状态位。这可以防止恶意或错误的长帧耗尽缓冲区资源。RFTHR (接收帧阈值)这是一个用于优化中断性能的巧妙设计。如果将其设置为NN1则SCC会在累积接收到N个完整帧后才产生一次RXF接收帧中断而不是每收到一帧就中断一次。这对于处理高速、短帧数据流如SS7信令至关重要可以大幅降低CPU的中断响应负担。HMASK (地址掩码) 与 HADDR1-4 (地址寄存器)这五个寄存器共同构成了HDLC帧的地址过滤机制。SCC会将接收到的帧地址字段与HADDR1-4中的值进行比较比较前会先与HMASK进行“与”操作。HMASK中为1的位表示需要严格匹配为0的位则被忽略“不关心”位。例如要匹配8位地址0x55可以设置HADDR1 0x0055,HMASK 0x00FF高8位忽略。最多可设置4个匹配地址或广播地址。由SCC硬件维护的计数器DISFC,CRCEC,ABTSC,NMARC,RETRC这五个16位计数器分别用于统计丢弃帧、CRC错误帧、中止序列、地址不匹配帧和重传帧的数量。它们在调试阶段是无价之宝可以通过定期读取这些计数器来评估链路质量。3.2 发送与接收缓冲区描述符深度解析缓冲区描述符是SCC与CPU交互的核心契约。每个BD占用8个字节包含状态控制和数据指针信息。接收缓冲区描述符 (RX BD) 关键字段E (空位)1表示此BD及其关联的数据缓冲区为空归SCC所有SCC可以往里存数据。当SCC填满缓冲区或遇到错误时会将其清零。CPU只有在E0时才能安全地读取缓冲区数据。W (回绕位)1表示此BD是BD表中的最后一个。SCC处理完它后会自动跳回RBASE指向的第一个BD形成环形队列。I (中断位)1表示当SCC使用关闭此BD时应产生RXB接收缓冲区中断。注意RXF接收帧中断的产生与此位无关由RFTHR控制。L (帧末位)1表示此缓冲区包含一个帧的最后一个数据包括CRC。此时DATA LENGTH字段记录的是整个帧的字节数含CRC。F (帧首位)1表示此缓冲区包含一个帧的开始部分起始标志后的第一个字节。状态位 (CD, OV, AB, NO, CR, LG)分别指示载波丢失、接收溢出、中止序列、字节对齐、CRC错误和帧过长错误。一个BD可以同时报告多个错误例如可能同时发生溢出和CRC错误。发送缓冲区描述符 (TX BD) 关键字段R (就绪位)CPU将此位置1表示数据已准备好交给SCC发送。SCC发送完成后会将其清零。W, I功能同RX BD分别控制回绕和中断TXB发送缓冲区中断。L (帧末位)1表示此缓冲区是当前帧的最后一个缓冲区。TC (发送CRC)仅当L1时有效。0表示在数据后直接发送结束标志用于测试发送错误CRC1表示在数据后附加CRC序列再发送结束标志正常操作。状态位 (UN, CT)分别指示发送欠载数据供给不上和CTS信号丢失错误。实操心得在初始化BD表时一个常见的错误是忘记设置第一个BD的W位或者整个环形链表的W位设置逻辑错误导致SCC处理完一个BD后不知下一步该去哪从而停止工作。务必在编码时仔细检查BD链表的链接关系。通常的做法是在内存中连续分配N个BD将前N-1个的W位设为0最后一个的W位设为1。4. SCC HDLC模式寄存器配置与命令详解4.1 协议特定模式寄存器 (PSMR) 配置策略PSMR寄存器定义了HDLC模式下的具体行为其配置需要根据实际应用场景仔细考量。NOF (标志数量)定义帧间或帧前插入的最小标志0x7E数量。设置为0可实现背靠背帧仅共享一个标志。在某些对带宽利用率要求极高的场景如SS7会设置为0并启用FSE标志共享使能位。CRC (CRC选择)选择16位还是32位CRC。绝大多数传统HDLC应用使用16位CCITT-CRC。32位CRC提供更强的检错能力常用于对数据完整性要求极高的环境。RTE (重传使能)当CTS信号在发送帧的第一或第二个缓冲区期间丢失时是否启用自动重传。这在半双工或多点链路中防止数据碰撞非常有用。启用此功能时务必确保帧的前两个数据缓冲区足够大手册建议36字节否则重传机制可能无法正常工作。MFF (FIFO中多帧)允许发送FIFO中包含多个HDLC帧。这可以提高小帧背靠背发送时的效率减少帧间标志。但代价是如果发生CTS丢失错误可能无法精确定位到具体是哪个帧/缓冲区出的问题。在需要精确错误定位的调试阶段建议关闭此功能。4.2 SCC HDLC控制器命令与使用场景CPM命令寄存器CPCR用于向SCC发送控制命令这些命令是动态管理SCC行为的直接手段。STOP TRANSMIT / GRACEFUL STOP TRANSMIT两者都用于停止发送。STOP TRANSMIT是“急刹车”会立即中止当前帧的发送最多再发64比特后并发送中止序列。GRACEFUL STOP TRANSMIT是“优雅停车”会等待当前帧发送完毕后再停止。后者常用于需要插入高优先级帧但又不想破坏当前正在传输帧的场景。发出GRACEFUL STOP TRANSMIT后需要等待SCCE寄存器中的GRA位置位确认发送已完全停止才能安全修改发送参数或BD表。RESTART TRANSMIT在发送被STOP TRANSMIT或错误停止后使用此命令重启发送。SCC会从当前TBPTR指向的BD继续发送。ENTER HUNT MODE强制接收器立即停止当前帧的接收进入“狩猎”模式重新开始搜索起始标志。这在协议栈需要重置链路状态或丢弃无效数据时非常有用。INIT TX/RX PARAMETERS初始化发送或接收参数RAM到复位状态。必须在发送器/接收器禁用时才能执行此命令。注意事项命令的发送需要遵循严格的时序。例如在发送GRACEFUL STOP TRANSMIT后必须轮询或中断检查GRA位是否置位确认操作完成才能进行后续操作如修改BD。盲目执行下一步操作会导致不可预知的行为。5. 完整编程实践从零搭建一个HDLC通信链路理论说得再多不如动手调一遍。下面我将结合手册中的示例和实际工程经验详细拆解一个典型的SCC2 HDLC通道初始化与数据收发流程。我们假设场景是使用外部时钟CLK3启用RTS/CTS流控进行点对点全双工通信。5.1 硬件引脚与时钟配置这是最容易出错的第一步。MPC823的引脚功能是复用的必须正确配置相关寄存器将特定引脚“映射”给SCC2使用。// 1. 配置端口A使能TXD2发送和RXD2接收引脚 // PAPAR[13:12] 1 (引脚作为SCC2功能) // PADIR[13:12] 0 (方向为输入由SCC控制) // PAODR[13:12] 0 (默认为0开漏输出禁用) *(volatile uint16_t*)PAPAR | (1 13) | (1 12); *(volatile uint16_t*)PADIR ~((1 13) | (1 12)); // PAODR通常默认0无需操作 // 2. 配置端口C使能RTS2请求发送、CTS2清除发送、CD2载波检测 // PCPAR[14]1 (RTS2作为功能引脚), PCPAR[9:8]0 (CTS2, CD2作为通用I/O) // PCDIR[14,9,8]0 (方向输入) // PCSO[9:8]1 (将CTS2, CD2配置为外设功能输入) *(volatile uint16_t*)PCPAR | (1 14); *(volatile uint16_t*)PCPAR ~((1 9) | (1 8)); *(volatile uint16_t*)PCDIR ~((1 14) | (1 9) | (1 8)); *(volatile uint16_t*)PCSO | (1 9) | (1 8); // 3. 配置端口A使能CLK3引脚作为外部时钟输入 *(volatile uint16_t*)PAPAR | (1 5); *(volatile uint16_t*)PADIR ~(1 5); // 4. 在串行接口配置寄存器(SICR)中将CLK3连接到SCC2的接收和发送时钟 // 假设SICR地址为0xFFFFFxxx需查阅具体手册。设置R2CS和T2CS字段为110b选择CLK3 uint32_t sicr_val *(volatile uint32_t*)SICR_BASE; sicr_val ~((0x7 R2CS_OFFSET) | (0x7 T2CS_OFFSET)); // 先清零 sicr_val | ((0x6 R2CS_OFFSET) | (0x6 T2CS_OFFSET)); // 设置为110b *(volatile uint32_t*)SICR_BASE sicr_val; // 5. 确保SCC2连接到NMSI非复用串行接口即使用独立的引脚 // 清除SICR中的SC2位如果存在具体取决于型号和模式5.2 参数RAM与缓冲区描述符初始化接下来是重头戏在双端口RAM中设置SCC2 HDLC的参数和BD表。假设我们为SCC2分配的参数RAM基址SCC2_BASE为0x2000在CPM内部RAM中。// 6. 设置SDMA总线仲裁级别通常使用默认或设置为5 *(volatile uint16_t*)SDCR 0x0001; // 设置仲裁级别为5 // 7. 设置接收和发送BD表基址 // 假设RX BD从0x2000开始TX BD从0x2008开始每个BD 8字节 *(volatile uint16_t*)(SCC2_BASE RBASE_OFFSET) 0x2000; *(volatile uint16_t*)(SCC2_BASE TBASE_OFFSET) 0x2008; // 8. 执行INIT RX AND TX PARAMS命令使上述RBASE/TBASE生效 issue_cpm_command(CPCR, CMD_INIT_RX_TX_PARAMS | CHAN_SCC2); // 9. 配置FIFO控制寄存器通常为正常操作模式 *(volatile uint8_t*)(SCC2_BASE RFCR_OFFSET) 0x18; *(volatile uint8_t*)(SCC2_BASE TFCR_OFFSET) 0x18; // 10. 设置最大接收缓冲区长度(MRBLR)例如256字节 *(volatile uint16_t*)(SCC2_BASE MRBLR_OFFSET) 0x0100; // 11. 12. 配置16位CRC常数和预设值 *(volatile uint32_t*)(SCC2_BASE C_MASK_OFFSET) 0x0000F0B8; *(volatile uint32_t*)(SCC2_BASE C_PRES_OFFSET) 0x0000FFFF; // 13. 清零错误计数器可选便于调试观察 *(volatile uint16_t*)(SCC2_BASE DISFC_OFFSET) 0; *(volatile uint16_t*)(SCC2_BASE CRCEC_OFFSET) 0; // ... 清零其他计数器ABTSC, NMARC, RETRC // 14. 设置最大帧长度(MFLR)例如与MRBLR一致为256字节 *(volatile uint16_t*)(SCC2_BASE MFLR_OFFSET) 0x0100; // 15. 设置接收帧阈值(RFTHR)例如每收到1帧就中断 *(volatile uint16_t*)(SCC2_BASE RFTHR_OFFSET) 0x0001; // 16. 17. 配置地址匹配允许所有地址掩码全0地址寄存器清零 *(volatile uint16_t*)(SCC2_BASE HMASK_OFFSET) 0x0000; *(volatile uint16_t*)(SCC2_BASE HADDR1_OFFSET) 0; // ... HADDR2, HADDR3, HADDR4 // 18. 初始化第一个接收BD // RX BD状态字: E1 (空SCC可写), W1 (假设只有一个BD形成自环), I1 (使能缓冲区中断) // 数据缓冲区指针指向主存0x00001000 volatile uint16_t *rx_bd_status (volatile uint16_t*)0x2000; volatile uint16_t *rx_bd_length (volatile uint16_t*)0x2002; volatile uint32_t *rx_bd_pointer (volatile uint32_t*)0x2004; *rx_bd_status 0xB000; // 二进制: 1011 0000 0000 0000 (E1, W1, I1) *rx_bd_length 0; // SCC会写入实际长度 *rx_bd_pointer 0x00001000; // 19. 初始化第一个发送BD准备发送一个5字节的帧 // TX BD状态字: R0 (未就绪), W1, I1, L1 (最后一帧), TC1 (发送CRC) // 数据缓冲区指针指向主存0x00002000长度5 volatile uint16_t *tx_bd_status (volatile uint16_t*)0x2008; volatile uint16_t *tx_bd_length (volatile uint16_t*)0x200A; volatile uint32_t *tx_bd_pointer (volatile uint32_t*)0x200C; *tx_bd_status 0x3C00; // 二进制: 0011 1100 0000 0000 (W1, I1, L1, TC1) *tx_bd_length 5; *tx_bd_pointer 0x00002000; // 将要发送的5个字节数据写入0x00002000开始的内存 uint8_t *tx_data (uint8_t*)0x00002000; tx_data[0] 0x01; // 示例数据 tx_data[1] 0x02; tx_data[2] 0x03; tx_data[3] 0x04; tx_data[4] 0x05;5.3 寄存器最终配置与通道使能完成内存数据结构的初始化后最后配置SCC的各个控制寄存器并打开收发使能开关。// 20. 清除SCC事件寄存器中所有可能的历史事件 *(volatile uint16_t*)(SCCE_HDLC_BASE) 0xFFFF; // 21. 配置SCC掩码寄存器使能我们关心的事件中断 // 例如使能TXE发送错误、RXF接收帧、TXB发送缓冲区中断 *(volatile uint16_t*)(SCCM_HDLC_BASE) 0x001A; // 对应位图 // 22. 配置CPM中断控制器将SCC2中断映射到系统中断向量 // 这涉及CIMR和CICR寄存器与具体系统中断设计相关此处略去细节 // *(volatile uint32_t*)CIMR ... ; // 23. 24. 配置GSMR_H和GSMR_L寄存器 // GSMR_H: 通常配置流控引脚行为、帧间空闲/标志等 *(volatile uint32_t*)GSMR_H_SCC2 0x00000000; // 例如正常模式帧间发空闲符 // GSMR_L: 配置模式为HDLC时钟源是否反相等。注意先不使能ENT和ENR *(volatile uint32_t*)GSMR_L_SCC2 0x00000000; // HDLC模式正常时钟不反相 // 25. 配置PSMR-HDLC寄存器 // 例如1个标志16位CRC禁止FIFO多帧 *(volatile uint16_t*)PSMR_HDLC_SCC2 0x0000; // 26. 最后再次写入GSMR_L置位ENT和ENR位使能发送器和接收器 // 这是一个关键步骤确保其他配置生效后再打开通道 uint32_t gsmr_l_val *(volatile uint32_t*)GSMR_L_SCC2; gsmr_l_val | (1 ENT_BIT_POS) | (1 ENR_BIT_POS); *(volatile uint32_t*)GSMR_L_SCC2 gsmr_l_val; // 27. 将发送BD置为就绪启动发送 *tx_bd_status | 0x8000; // 将R位置1至此SCC2 HDLC通道初始化完成。发送器会立即开始发送0x00002000处的5字节数据封装成HDLC帧。接收器则开始监听线路等待起始标志并将收到的数据存入0x00001000开始的缓冲区。6. 中断服务程序与数据流管理实战硬件初始化只是开始一个健壮的HDLC驱动核心在于其中断服务程序ISR的设计。ISR需要高效地处理收发完成事件重置BD并准备好下一次操作。6.1 发送完成处理当一帧数据发送完毕SCC会根据TX BD中的I位设置触发TXB中断或在错误时触发TXE中断。在ISR中读取SCCE寄存器判断中断源。如果是TXB检查对应的TX BD状态字。确认R位已被SCC清零L位已设置如果是帧的最后一个BD。检查错误位UN,CT。如果有错误进行相应的错误处理如重试、日志记录。将已发送BD的数据缓冲区指针和长度字段“回收”以便填入新的待发送数据。如果需要连续发送则准备下一个TX BD填充数据设置R1。如果是环形队列的最后一项要确保正确跳转。清除SCCE寄存器中对应的中断标志位写1清零。6.2 接收完成处理当收到完整的一帧或达到RFTHR阈值SCC会触发RXF中断。对于每个接收到的缓冲区RXB中断处理流程类似但更关注数据读取SCCE寄存器。遍历接收BD表找到E位为0的BD表示已被SCC填充。仔细检查BD状态字L位判断是否为一帧的结束F位判断是否为一帧的开始重点检查错误位CD,OV,AB,NO,CR,LG。不同的错误需要不同的上层处理策略。例如CR错误通常直接丢弃该帧OV错误可能提示需要优化缓冲区大小或提高CPU中断响应速度。从DATA LENGTH字段获取有效数据长度如果L1此为整个帧长含CRC否则为此缓冲区数据长度。根据RX DATA BUFFER POINTER读取数据缓冲区内容。注意如果启用了CRC校验帧的最后2或4个字节是CRC上层协议可能需要剥离。处理完数据后必须将该BD重新“交还”给SCC清除状态字中的所有状态位写0然后将E位置1。这是最关键的步骤忘记置E位会导致SCC无法继续接收链路“卡死”。清除SCCE中的中断标志。避坑指南一个极其常见且隐蔽的Bug是“BD描述符链断裂”。尤其是在多缓冲区处理一帧的情况下必须确保最后一个BD的L位被正确设置。如果SCC在发送或接收时找不到L1的BD它会认为帧尚未结束从而持续等待或错误地处理数据导致通信异常。在调试时可以借助仿真器或通过内存查看器实时监控BD表中各个BD状态字的变化这是定位问题的有效手段。7. 高级主题与性能优化技巧7.1 使用DPLL与曼彻斯特编码手册中的第二个编程示例展示了如何使用片内数字锁相环DPLL和曼彻斯特编码。这在某些没有独立时钟线的同步串行协议如某些工业现场总线中非常有用。关键步骤在于GSMR_L的配置设置DPLL相关位选择时钟模式如16倍频。设置ENC编码位为曼彻斯特。此时外部只需提供比特率的16倍时钟到CLK引脚DPLL会从中恢复出接收时钟和数据。7.2 流控与错误恢复策略RTS/CTS流控通过配置GSMR_L的TENC和RENC位为自动模式SCC可以硬件自动管理RTS和CTS信号实现可靠的硬件流控。当接收缓冲区快满时SCC会拉低RTS通知对端暂停发送。自动重传在多点竞争总线如HDLC总线模式中使能PSMR的RTE位和配置BUS模式SCC可以在检测到CTS丢失即发生碰撞时自动重传当前帧无需CPU干预。接收帧阈值(RFTHR)调优对于高频短帧应用将RFTHR设置为一个大于1的值如4或8可以成倍减少中断次数显著提升系统吞吐量。但需要权衡带来的延迟增加。7.3 调试与诊断手段当通信不通时按以下顺序排查物理层首先用示波器或逻辑分析仪检查TXD、RXD、CLK、CTS、RTS引脚是否有信号电平、时序是否正确。配置检查确认所有步骤的寄存器配置值特别是GSMR_L的模式位、PSMR的CRC选择、参数RAM的CRC常数。一个十六进制数写错就可能导致全盘失败。BD状态在调试器中查看发送和接收BD的状态字。发送时BD的R位是否被清零接收时BD的E位是否被清零错误位是否有置起中与事件检查SCCE寄存器看是否有预期的事件标志置位。检查CPM和全局中断控制器是否已正确使能SCC中断。计数器查看参数RAM中的错误计数器CRCEC,ABTSC等它们能告诉你链路层正在发生什么类型的错误。我个人在多年的嵌入式通信开发中最深的一点体会是理解数据流和状态机比记忆寄存器地址更重要。MPC823的SCC HDLC控制器是一个高度自动化的状态机我们的编程本质上是为这个状态机设置初始状态和提供资源缓冲区并在其状态变迁完成、错误时进行干预。画一张数据流和BD状态变迁图往往比盯着代码看半天更管用。最后充分利用硬件提供的丰富状态和错误报告构建详细的日志系统是在复杂现场环境中快速定位问题的基石。

相关新闻