在一些特别情况下,我们会碰到 USB 兼容性问题。比如,我正在使用的 DELL C2722DE 显示器,通过 TypeC 连接主机,除了能够显示之外,还内置了 USB Hub ,但是它和我的微软鼠标存在兼容性问题,如果插着显示器休眠,或者开机的话,进入 Windows之后鼠标是无法直接使用,必须插拔一次才能正常工作。
针对这种情况,这次设计了一个 USB 插拔装置,遇到问题的时候无需做插拔动作,而是通过按键一次自行完成一个插拔的模拟,这样可以方便使用。USB插拔动作可以分解为2步,插入时是先接通5V供电,然后接通D+/D-信号。如果仔细观察USB接头,可以发现USB D+/D- 引脚会比5V和GND 短一点,这样就能保证插入的时候是先接通供电,然后再接入信号的。这样可以避免插入时信号先于供电接通,电流直接倒灌到芯片中。类似的,拔出时,是先断开信号,然后再切断供电。
硬件方面使用了3个芯片:CH554 /CH442/SY6280AAC。CH554是一个单片机,这里作用是获得按钮状态,然后根据状态控制USB信号的切换和USB母头上的5V输出; CH442进行信号切换, SY6280AAC 是功率电子开关芯片,这里我们用用它控制USB供电输出。
CH442是一款低阻宽带双向模拟开关芯片,包含2路单刀双掷二选一开关。这里用作 USB 信号二选一。


PCB 设计如下:

焊接后的成品,刚好能装在外壳中:

CH554 外围只需要1个10K电阻,2个0.1uf 电容即可让它工作起来。然后通过P1 Header 4 下载数据非常方便。此外,CH_IN 适用于控制CH442信号切换,CTRL1用于控制SY6280AAC。
代码部分非常简单:
#define CH_IN 14
#define LED 15
#define CTRL1 16
#define KEY 17
void setup() {
pinMode(CH_IN,OUTPUT);
pinMode(LED,OUTPUT);
pinMode(CTRL1,OUTPUT);
pinMode(KEY,INPUT_PULLUP);
digitalWrite(CH_IN,LOW); // Switch to 1
digitalWrite(LED,LOW); // 不亮
digitalWrite(CTRL1,HIGH); // 供电
}
unsigned long int Elsp=0;
void loop() {
if (digitalRead(KEY)==LOW) {
digitalWrite(LED,HIGH); // 点亮 LED
digitalWrite(CH_IN,HIGH); // 断开数据线
delay(100);
digitalWrite(CTRL1,LOW); // 断开USB供电
Elsp=millis();
} else {
// Key 抬起
if ((Elsp!=0)&&(millis()-Elsp>500)) {
Elsp=0;
digitalWrite(LED,LOW); // 关闭 LED
digitalWrite(CTRL1,HIGH); // 开始USB供电
delay(100);
digitalWrite(CH_IN,LOW); // 接通数据线
}
}
}
适用范围:如果你遇到的问题通过插拔一次可以解决,并且出现问题的时候USB端口能够正常供电,那么可以尝试本次的设计。运气好的话,因为引入了切换元件使得USB信号发生变化,每次都可以直接用;稍微差一些的话,出现问题的时候按下按钮即可模拟插拔让设备工作起来。