news 2026/11/6 3:00:43

I2C多主机仲裁与时钟延展:开漏输出下的精妙设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
I2C多主机仲裁与时钟延展:开漏输出下的精妙设计

1. 为什么多主机仲裁是I2C最被低估的设计

搞嵌入式的人对I2C都不陌生,两根线、一个上拉电阻、一堆设备挂在上面,看起来简单得不行。但真正让我觉得I2C设计精妙的,不是它省引脚,而是它处理多主机竞争的方式。你想想看,多个主机同时想说话,放在别的总线上要么需要一个中央裁判,要么就得靠复杂的令牌机制。I2C没有裁判,没有令牌,就靠两根开漏线和一套“线与”逻辑,硬是把仲裁这件事做得干干净净。

我第一次认真研究这个机制,是因为一个实际项目里出现了两个MCU同时访问同一片EEPROM的场景。当时想当然地以为“谁先发起谁先赢”,结果发现数据偶尔会错乱。后来用逻辑分析仪抓波形才明白,I2C的仲裁不是先到先得,而是边发边裁——每个主机在发送每一位的同时都在监听总线,一旦发现自己发的和总线上的不一致,立刻退出。这个过程不需要额外引脚,不需要协商协议,完全融入在正常的时序里。

这篇文章我会把多主机仲裁和时钟延展这两个机制拆开揉碎讲清楚。不管你是刚接触I2C的新手,还是已经用过几十片I2C设备的老手,我相信里面总有一些细节是你之前没注意到的。尤其是那些“为什么这么设计”的部分,理解了之后,你再看I2C的时序图会有完全不同的感受。

2. 开漏输出:仲裁机制能成立的物理基础

2.1 开漏输出到底“开”了什么

要理解仲裁,必须先理解开漏输出。很多教程一上来就讲“开漏就是只能拉低不能拉高”,这话没错,但没说到点子上。开漏输出的本质是:输出级只有一个NMOS管接到地,栅极由内部信号控制,漏极接到外部引脚。当内部信号为高时,NMOS导通,引脚被拉到地;当内部信号为低时,NMOS截止,引脚处于高阻态,此时靠外部上拉电阻把电平拉高。

这个结构带来的关键特性是:任何一个设备都只能主动把线拉低,不能主动把线拉高。高电平是所有设备都“放手”之后,由上拉电阻自然形成的。这就好比一间会议室里所有人都有权拍桌子喊停,但没人有权强行让别人闭嘴——只要还有一个人拍着桌子,整个房间就是“停”的状态。

用电路的语言说,这就是线与逻辑:只要有一个输出为低,总线就是低;只有所有输出都为高阻,总线才被上拉电阻拉高。这个逻辑是仲裁机制的全部基础。

2.2 为什么推挽输出会毁掉仲裁

推挽输出和开漏输出最大的区别在于:推挽输出的高电平是主动驱动的,内部PMOS管导通,把引脚强行拉到VCC。如果两个推挽输出的设备一个想输出高、一个想输出低,那就直接形成VCC到GND的低阻通路,轻则大电流烧毁引脚,重则整片芯片报废。

我见过不少初学者在调试I2C时,因为MCU的GPIO配置成了推挽模式,结果通信时好时坏,甚至芯片发烫。原因就在这里:推挽模式下,主机发送高电平时会主动驱动总线为高,而如果此时从机正在拉低总线做应答,两边就打架了。开漏模式下这种冲突不存在,因为“发高”实际上就是“放手”,不会和任何人的拉低动作冲突。

所以记住一条铁律:I2C的SDA和SCL必须配置为开漏输出(或开漏加外部上拉),绝对不能配成推挽。STM32的HAL库在I2C初始化时会自动配置为开漏复用模式,但如果你用软件模拟I2C,GPIO模式一定要手动设对。

2.3 上拉电阻选型的实际计算

上拉电阻的取值不是随便拍脑袋的。它受两个因素制约:上升沿时间和功耗。

I2C标准模式(100kHz)要求上升沿时间不超过1000ns,快速模式(400kHz)要求不超过300ns。上升沿时间由RC充电曲线决定,公式是:

t_r ≈ 0.847 × R_p × C_bus

其中C_bus是总线电容,包括PCB走线电容、引脚电容和器件电容,一般估算为10pF到20pF每设备,加上走线大约50pF到200pF不等。

假设C_bus = 200pF,快速模式要求t_r ≤ 300ns,那么R_p ≤ 300ns / (0.847 × 200pF) ≈ 1.77kΩ。这是上限。下限则由功耗和灌电流能力决定:总线被拉低时,电流I = VCC / R_p,所有设备的灌电流加起来不能超过任何一个设备的最大灌电流(通常3mA)。如果VCC = 3.3V,R_p = 1.77kΩ,电流约1.86mA,在安全范围内。

实际项目中,我一般先用4.7kΩ试,如果波形上升沿太慢就降到2.2kΩ,如果功耗敏感就升到10kΩ。但10kΩ在400kHz下基本不能用,上升沿会拖得很长,导致时序违规。

3. 多主机仲裁:逐位竞争,输家自动退场

3.1 仲裁发生在哪些时刻

多主机仲裁不是在整个数据帧层面进行的,而是在每一个bit上实时进行。具体来说,仲裁发生在两种时刻:一是起始条件(START)之后,多个主机同时开始发送地址;二是重复起始条件(Repeated START)之后,多个主机同时想切换方向或继续通信。

仲裁的过程是这样的:每个主机在发送每一位时,都会把自己的SDA输出和实际读回的SDA电平做比较。如果自己发的是1(放手),但读回来是0(被别人拉低),说明有另一个主机在发0,自己输了,立刻停止发送SDA和SCL,转为从机接收模式。如果自己发的是0,读回来也是0,那没问题,继续。如果自己发的是1,读回来也是1,也继续。

关键在于:仲裁只发生在SDA线上,SCL线不参与仲裁。这是因为SCL的同步机制保证了所有主机在同一个时钟节拍上采样,后面会详细讲。

3.2 一个具体的仲裁过程推演

假设主机A和主机B几乎同时发起通信。主机A想访问地址0x50,主机B想访问地址0x48。

地址字节是7位地址加1位读写位。0x50的二进制是1010000,0x48是1001000。加上写位0,A发送的是10100000,B发送的是10010000。

从最高位开始:

  • 第1位:A发1,B发1,总线为1,都继续。
  • 第2位:A发0,B发0,总线为0,都继续。
  • 第3位:A发1,B发0。A放手,总线被B拉低为0。A读回0,发现自己发的是1但读回0,A输,退出。B继续。

整个过程在第3位就完成了仲裁,B赢得总线,A自动转为接收模式,等待下一次总线空闲。注意,A退出时不会产生任何额外的信号,总线上看不出任何异常,B甚至不知道曾经有竞争。

这就是I2C仲裁的精妙之处:输家无声退场,赢家毫无感知。整个过程不需要额外的仲裁线,不需要仲裁协议,完全融入在正常的位传输中。

3.3 仲裁失败的几种典型场景

仲裁失败不一定只发生在地址阶段。以下几种情况都会触发仲裁:

  • 地址不同:如上例,高位先分出胜负。
  • 读写位不同:两个主机访问同一地址,但一个读一个写,在第8位分出胜负。
  • 数据不同:两个主机访问同一从机,地址和读写位都相同,但在数据阶段发送了不同的值。
  • 重复起始条件冲突:一个主机想发重复起始,另一个想发停止,在SDA和SCL的特定组合上分出胜负。

我遇到过一种比较隐蔽的情况:两个主机访问同一个从机,地址相同,读写位相同,数据也大部分相同,但在某个字节的某一位上不同。这种情况下,仲裁会一直进行到那一位才分出胜负,前面的通信看起来完全正常。用逻辑分析仪抓波形时,如果不仔细看SDA的细微变化,很难发现曾经发生过仲裁。

3.4 仲裁失败后主机的行为

仲裁失败的主机必须立即做三件事:

  1. 停止驱动SDA:转为输入模式,不再输出。
  2. 停止驱动SCL:但要注意,如果它正在拉低SCL,必须等到SCL实际变高之后才能放手,否则会干扰赢家的时钟。
  3. 切换到从机接收模式:因为它可能被赢家寻址(如果赢家访问的地址恰好是它的地址),所以必须准备好接收。

这里有一个容易忽略的细节:仲裁失败的主机在退出时,不能产生停止条件。它只是静静地放手,让赢家继续。如果它错误地产生了停止条件,整个通信就乱了。

4. 时钟同步:让所有主机步调一致

4.1 时钟同步的物理机制

多主机环境下,每个主机都有自己的SCL时钟。如果各发各的,总线就乱了。I2C的解决方案是时钟同步:所有主机的SCL输出也是开漏的,通过线与逻辑连在一起。总线SCL的实际电平是所有主机SCL输出的“与”结果。

具体来说,每个主机在拉低SCL之后,会开始一个低电平计时。当它想释放SCL时,会先检查SCL是否真的变高了。如果另一个主机还在拉低,SCL保持低,这个主机就会进入等待状态,直到SCL真正变高才开始高电平计时。

这就形成了一个有趣的局面:低电平周期由拉低时间最长的主机决定,高电平周期由拉高时间最短的主机决定。换句话说,时钟同步后的SCL频率,等于所有主机中最慢的那个。

4.2 时钟同步与仲裁的配合

时钟同步和仲裁是两套独立但配合的机制。仲裁决定谁赢得总线,时钟同步保证所有主机在同一个节拍上采样。

为什么SCL不参与仲裁?因为如果SCL也参与仲裁,那么一个主机拉低SCL时,另一个主机如果发高就会输,但发高的主机可能只是想让时钟快一点,并不想争夺总线。这样会导致不必要的仲裁失败。所以I2C规定:SCL只同步,不仲裁;SDA既同步又仲裁。

这个设计的好处是:即使一个主机在仲裁中输了,它仍然在参与时钟同步,直到它完全退出。这保证了赢家的时钟不会被输家的SCL拉低所干扰。

4.3 时钟延展:从机的“暂停键”

时钟延展(Clock Stretching)是I2C另一个精妙设计。它允许从机在需要更多时间处理数据时,主动拉低SCL,迫使主机等待。

从机的SCL引脚也是开漏的。当从机需要延展时钟时,它会在主机释放SCL之后、下一个时钟周期之前,把SCL拉低。主机检测到SCL没有按预期变高,就会进入等待状态,直到从机释放SCL。

这个机制解决了一个很实际的问题:从机可能是一个慢速的ADC、一个需要时间擦写的EEPROM、或者一个处理能力有限的MCU。如果没有时钟延展,主机就必须按照最慢从机的速度来设计时钟,或者从机必须用足够快的缓冲来跟上主机。有了时钟延展,从机可以在需要时“喊停”,主机乖乖等着。

4.4 时钟延展的实际应用与坑

我在一个项目里用过一款温湿度传感器,它的转换时间最长需要50ms。如果主机用400kHz的时钟连续读取,传感器根本来不及更新数据。解决方案就是让传感器在转换期间拉低SCL,主机检测到SCL被拉低后进入等待,直到传感器释放SCL才继续读取。

但时钟延展也有坑。最大的坑是:不是所有主机都支持时钟延展。有些硬件I2C控制器在检测到SCL被意外拉低时,会报总线错误或者超时,而不是耐心等待。我在STM32的某些系列上就遇到过这个问题,需要配置超时时间或者改用软件I2C。

另一个坑是:时钟延展可能被误判为总线挂死。如果从机因为某种原因一直拉低SCL不放,主机就会一直等下去。所以实际项目中,主机端通常需要加一个超时机制,比如等待超过10ms就复位I2C控制器。

5. 用逻辑分析仪看仲裁和时钟延展的真实波形

5.1 抓取仲裁过程的配置要点

要看仲裁波形,逻辑分析仪的采样率至少要是SCL频率的10倍以上。100kHz的I2C,采样率至少1MHz;400kHz的,至少4MHz。我一般用10MHz采样率,这样能看清上升沿和下降沿的细节。

触发条件设置成SDA下降沿(起始条件),然后开启协议解码。逻辑分析仪的I2C解码器会自动标注START、ADDR、R/W、ACK、DATA、STOP等字段。如果发生仲裁,你会看到总线上只有一个完整的通信过程,输家的信号完全消失。

5.2 仲裁波形的特征

仲裁发生时,SDA线上的波形有一个明显特征:在某个bit位上,SDA的下降沿比正常情况更早或更晚。这是因为两个主机同时驱动SDA,一个拉低一个放手,实际波形是两者的线与结果。

具体来说,如果主机A发1(放手),主机B发0(拉低),SDA会在B拉低的时刻下降。A在采样点读回0,发现自己输了,立即停止驱动SDA。从波形上看,SDA在这个bit位上的下降沿是由B产生的,A的信号消失得无影无踪。

用逻辑分析仪看的时候,如果只看解码结果,你甚至看不出发生过仲裁。只有对比两个主机的代码或者同时抓两个主机的GPIO,才能确认仲裁确实发生了。

5.3 时钟延展的波形特征

时钟延展的波形特征更明显:SCL的低电平周期被拉长。正常时钟的低电平时间是固定的,但当时钟延展发生时,SCL会在主机释放之后继续保持低,直到从机释放。

用逻辑分析仪看的时候,你会看到SCL的低电平时间明显超过正常值,而SDA在这段时间内保持不变。协议解码器通常会把这段延展标记出来,或者至少你能从时间轴上看出异常。

我一般会同时抓SCL和SDA,然后测量每个SCL低电平周期的宽度。如果某个周期的宽度明显大于其他周期,那就是时钟延展。

6. 常见问题与排查技巧实录

6.1 仲裁失败导致的数据错乱

现象:两个主机访问同一从机,偶尔读到错误数据。

排查思路:先用逻辑分析仪抓波形,确认是否发生了仲裁。如果仲裁发生在数据阶段,输家的数据可能已经被部分写入从机,导致从机状态不一致。

解决方法:在应用层加互斥机制,比如用一根额外的GPIO做总线请求信号,或者用软件令牌。如果必须依赖硬件仲裁,确保两个主机的通信内容不会互相干扰,比如访问不同的从机地址或不同的寄存器。

6.2 时钟延展导致的通信超时

现象:主机读取从机时,偶尔报超时错误。

排查思路:检查从机是否支持时钟延展,以及主机的超时设置是否足够长。用逻辑分析仪测量SCL低电平的最大宽度。

解决方法:如果从机确实需要长时间延展,增加主机的超时时间。如果主机不支持时钟延展,改用软件I2C或者换一款支持的主机。

6.3 上拉电阻过大导致上升沿太慢

现象:通信不稳定,偶尔出现ACK错误。

排查思路:用示波器测量SDA和SCL的上升沿时间。如果上升沿超过I2C标准的要求,就是上拉电阻过大。

解决方法:减小上拉电阻,或者减小总线电容(缩短走线、减少设备数量)。我一般会准备几种不同阻值的电阻,4.7kΩ、2.2kΩ、1kΩ,根据实际情况换。

6.4 总线挂死与恢复

现象:SCL或SDA被某个设备一直拉低,总线无法恢复。

排查思路:断电重启所有设备,逐个排查。用万用表测量SCL和SDA对地电阻,如果接近0,说明有设备短路。

解决方法:在主机端加总线恢复逻辑:发送9个时钟脉冲,让从机完成当前字节的传输并释放总线。如果还不行,复位I2C控制器或者重新初始化GPIO。

问题现象可能原因排查工具解决方法
数据偶尔错乱多主机仲裁逻辑分析仪应用层互斥或分离从机
通信超时时钟延展逻辑分析仪增加超时或换主机
ACK错误上升沿太慢示波器减小上拉电阻
总线挂死设备拉低不放万用表9时钟恢复或复位

7. 软件模拟I2C时如何正确实现仲裁与时钟延展

7.1 GPIO模式配置

软件模拟I2C时,SDA和SCL都必须配置为开漏输出,并且使能内部上拉(如果外部没有上拉)或者依赖外部上拉。在STM32的HAL库中,配置如下:

GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; // 开漏输出 GPIO_InitStruct.Pull = GPIO_PULLUP; // 内部上拉 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

注意:开漏输出模式下,写1实际上是释放总线,写0是拉低总线。读引脚状态用HAL_GPIO_ReadPin。

7.2 仲裁检测的实现

软件模拟时,仲裁检测需要在每个bit发送后读回SDA:

uint8_t i2c_send_bit(uint8_t bit) { if (bit) { SDA_RELEASE(); // 释放SDA,输出高阻 } else { SDA_LOW(); // 拉低SDA } SCL_HIGH(); // 释放SCL delay_us(1); uint8_t sda_read = SDA_READ(); // 读回SDA SCL_LOW(); // 拉低SCL delay_us(1); if (bit && !sda_read) { return 0; // 仲裁失败 } return 1; // 仲裁成功 }

如果返回0,说明自己发1但读回0,仲裁失败,立即停止发送并转为接收模式。

7.3 时钟延展的等待逻辑

软件模拟时,主机在释放SCL之后必须等待SCL真正变高:

void i2c_wait_scl_high(void) { uint32_t timeout = 10000; while (!SCL_READ() && timeout--) { delay_us(1); } if (timeout == 0) { // 超时处理,可能是总线挂死 i2c_bus_recovery(); } }

这个等待逻辑同时处理了时钟延展和总线挂死两种情况。如果等待超时,就执行总线恢复。

8. 硬件I2C控制器的仲裁与时钟延展支持情况

8.1 主流MCU的I2C控制器对比

MCU系列多主机仲裁时钟延展备注
STM32F1支持支持需要配置时钟延展使能
STM32F4支持支持超时机制完善
ESP32支持支持软件I2C更灵活
NXP LPC支持支持仲裁丢失中断
51单片机不支持不支持只能软件模拟

8.2 仲裁丢失中断的处理

很多硬件I2C控制器在仲裁失败时会产生中断。以STM32为例,仲裁丢失会置位I2C_SR1的ARLO位。在中断服务函数中,需要清除该标志并重新初始化I2C控制器:

void I2C1_EV_IRQHandler(void) { if (I2C1->SR1 & I2C_SR1_ARLO) { I2C1->SR1 &= ~I2C_SR1_ARLO; // 清除仲裁丢失标志 // 重新初始化I2C i2c_reinit(); } }

注意:仲裁丢失后,I2C控制器会自动切换到从机模式,所以重新初始化之前要确保总线已经空闲。

8.3 时钟延展的使能配置

有些硬件I2C控制器默认关闭时钟延展,需要手动使能。以STM32为例,在I2C初始化时设置I2C_CR1的NOSTRETCH位:

I2C1->CR1 &= ~I2C_CR1_NOSTRETCH; // 使能时钟延展

如果设置NOSTRETCH为1,从机模式下不会延展时钟,可能导致数据丢失。

9. 实际项目中的经验与避坑建议

9.1 多主机场景下的优先级设计

如果系统里确实有多个主机,我建议在应用层设计一个简单的优先级机制。比如给每个主机分配不同的地址段,或者用一根额外的GPIO做总线请求。硬件仲裁虽然精妙,但它是“公平”的——谁先发谁赢,没有优先级。如果某个主机需要优先访问,硬件仲裁帮不了你。

9.2 时钟延展的合理使用

时钟延展是从机的权利,但不要滥用。如果从机频繁延展时钟,主机的通信效率会大幅下降。我一般建议从机在必要时才延展,比如EEPROM的写周期、ADC的转换周期。对于可以缓冲的数据,从机应该先缓冲再让主机读取,而不是每次都延展。

9.3 总线电容的控制

总线电容是I2C稳定性的隐形杀手。每增加一个设备,就增加几pF的电容。如果设备多、走线长,上升沿会变得很慢。我的经验是:设备超过8个或者走线超过30cm,就要考虑用I2C缓冲器或者多路复用器。I2C多路复用器可以把一条总线分成多条,每条总线的电容独立计算,避免相互影响。

9.4 逻辑分析仪的选型建议

调试I2C,逻辑分析仪是必备工具。我推荐至少8通道、采样率100MHz以上的型号。便宜的逻辑分析仪(比如24MHz采样率的)在400kHz I2C下勉强够用,但看上升沿细节就不行了。如果预算有限,可以先买一个便宜的,等遇到瓶颈再升级。

9.5 软件I2C的适用场景

软件I2C虽然灵活,但有几个硬伤:速度慢、CPU占用高、仲裁和时钟延展需要手动实现。我一般只在以下场景用软件I2C:硬件I2C引脚被占用、需要非标准时序、从机不支持标准I2C。其他情况优先用硬件I2C。

10. 从仲裁和时钟延展看I2C的设计哲学

I2C的仲裁和时钟延展,表面上是两个独立的技术点,背后其实是同一种设计哲学:用最简单的物理机制解决复杂的协调问题。

开漏输出加线与逻辑,让多个设备可以共享一根线而不冲突。仲裁机制利用这个特性,让多个主机可以同时发起通信,输家自动退场。时钟延展利用同样的特性,让从机可以主动控制通信节奏。整个协议没有中央控制器,没有复杂的协商过程,所有协调都发生在电气层面。

这种设计哲学在今天的嵌入式系统里依然很有价值。当你面对一个多设备、多主机的通信需求时,I2C的方案往往比SPI、UART更简洁。当然,简洁是有代价的:速度慢、电容敏感、仲裁范围有限。但如果你理解了这些机制的原理,就能在合适的场景下发挥它的最大价值。

我在实际项目中用过I2C、SPI、UART、CAN等多种总线,I2C的仲裁和时钟延展是我觉得最值得反复琢磨的设计。每次重新看时序图,都会有新的理解。希望这篇文章能帮你把这些机制搞清楚,下次调试I2C时少走一些弯路。

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

车规MCU芯驰E3118 XIP模式下FEE开发实战与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/11/6 3:00:08

MCU晶振电路设计全指南:从负载电容计算到不起振排查

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/11/6 2:57:28

SAP ABAP批量建表实战:SE11数据字典自动化生成

上周又接了个活,对方甩过来一个 Excel,三十多个自定义表,每个表二三十个字段,字段全走域和数据元素,命名规范还卡得挺死。要是老老实实打开 SE11 一个对象一个对象点,域、数据元素、表三层走一遍&#xff0…

作者头像 李华
网站建设 2026/11/6 2:57:14

SQL分组聚合与数组转换实战指南

1. SQL中的分组聚合与数组转换在数据处理工作中,我们经常需要对数据进行分组聚合操作。SQL作为关系型数据库的标准查询语言,提供了强大的GROUP BY功能来实现数据分组。但有时候简单的聚合函数(如COUNT、SUM、AVG等)并不能满足我们…

作者头像 李华
网站建设 2026/11/6 2:54:44

期货当日成交量超过持仓量,是统计错了吗?

某个期货合约的当日成交量是3000手,当前持仓量只有1000手。两个数都以手计,为什么前者能比后者大?是不是把成交重复统计了? 这个疑问把当前持仓量当成了当日成交量的上限。实际上,一项描述一段时间里累计发生的成交&am…

作者头像 李华
网站建设 2026/11/6 2:54:09

Burp Suite Intruder模块:Web渗透测试自动化利器

1. Intruder模块:渗透测试中的自动化武器库在Web安全测试领域,效率往往决定着测试的深度和广度。当我第一次接触Burp Suite的Intruder模块时,就像发现了一把打开新世界大门的钥匙。这个工具彻底改变了我进行Web应用安全测试的方式&#xff0c…

作者头像 李华