最近我在使用 CH567 制作双 USB 设备,在这个过程中,我发现每次烧写程序都比较麻烦。例如,首先要拔掉设备,然后按下 DOWNLOAD 按键,接下来再插入USB端口中,最后才能烧写(值得庆幸的是我预留了RESET 按钮,否则还要重新插拔一次)。经过研究和实验,我设计了一个能够自动完成烧写的设备。基本原理是:使用芯片模拟USB设备下电过程(断开 D+、D-,再下电);然后通过MOSFET模拟按下DOWNLOAD键(Pin 下拉到GND);再使用芯片模拟USB设备上电过程(先上电,再连接 D+、D-);接下来用户就可以在PC上进行烧写;最后再通过MOSFET模拟RESET 键。烧写好的程序就可以正常运行了。
下面介绍这次用到的两颗主要芯片。首先是用于切换USB 信号(D+、D-)的CH442。它是 DPDT 模拟开关芯片,包含 2 路单刀双掷二选一开关。CH442 是CH44X 系列芯片的一款。这一系列是模拟开关芯片,具有高带宽,支持视频信号,支持低速、全速和高速 USB 信号的特点。
另外一个是 SY6280AAC芯片,用于控制 USB母头(连接CH567)的 USB 供电。
这次使用的主控为 DFRbot 出品的 FireBeetle,他的核心是 ESP32。实际上主要功能是通过GPIO来实现的,要求不高,有需要的朋友可以修改为任意的单片机。
最终电路图设计如下:
主控部分需要注意的是预留了一个跳线位置,必要时将此短接起来FireBeetle可以直接从USB1取电(例如,FireBeetle支持蓝牙,可以修改代码为蓝牙控制触发 CH567进去下载模式)。
下面是2个GPIO 通过MOSFET控制的RST和 PowerDown 引脚。没有触发时,RST和PWDPIN会出现3.3V,当有需要时可以拉低到GND。
接下来是USB接口和用于切换的CH442E 芯片,USB1 是USB公头,接入电脑中;USB3 是USB母头,用于连接CH567。CH442E控制USB1 的 USB信号(IN_D-、IN+D+)在USB3(DB、DC)和断开(S2B、S2C)之间切换。
最后是SY6280AA芯片,UPW_CTRL 控制IN_VCC 是否输出到 OUT_VCC 上。
这个设计是给FireBeetle 的Shield,最终的PCB 如下:
焊接之后照片如下,右侧是 DFRobot的FireBeetle,二者可以通过堆叠的方式进行连接。
完整代码如下:
#define PWD_CTRL 22
#define EN_CTRL 27
#define RST_CTRL 21
#define UPW_CTRL 14
#define IN_CTRL 26
void setup() {
Serial.begin(115200);
pinMode(PWD_CTRL, OUTPUT);
pinMode(EN_CTRL, OUTPUT);
pinMode(RST_CTRL, OUTPUT);
pinMode(UPW_CTRL, OUTPUT);
pinMode(IN_CTRL, OUTPUT);
digitalWrite(PWD_CTRL, LOW);
digitalWrite(EN_CTRL, LOW);
digitalWrite(RST_CTRL, LOW);
digitalWrite(UPW_CTRL, LOW);
digitalWrite(IN_CTRL, LOW);
}
void unplug() {
// USB1和USB3断开
digitalWrite(IN_CTRL, LOW);
Serial.print(IN_CTRL);
Serial.println("IN_CTRL LOW");
// CH567 断开5V
digitalWrite(UPW_CTRL, LOW);
Serial.print(UPW_CTRL);
Serial.println("UPW_CTRL LOW");
delay(20);
}
void plug() {
// CH567 上电
digitalWrite(UPW_CTRL, HIGH);
Serial.print(UPW_CTRL);
Serial.println("UPW_CTRL HIGH");
delay(20);
// USB1和USB3连通
digitalWrite(IN_CTRL, LOW);
Serial.print(IN_CTRL);
Serial.println("IN_CTRL HIGH");
}
void loop() {
if (Serial.available()) {
byte c = Serial.read();
// 按下 PowerDown
if (c == 'd') {
Serial.println("Press Power Down");
// 按下 PowerDown 键
digitalWrite(PWD_CTRL, HIGH);
Serial.println("PWD_CTRL HIGH");
}
// 抬起 PowerDown
if (c == 'u') {
Serial.println("Release Power Down");
//抬起 PowerDown 键
digitalWrite(PWD_CTRL, LOW);
Serial.println("PWD_CTRL HIGH");
}
// 按下抬起一次 RESET键
if (c == 'r') {
Serial.println("Press and Release RESET");
//按下 RESET 键
digitalWrite(RST_CTRL, HIGH);
Serial.println("RST_CTRL HIGH");
delay(20);
//抬起 PowerDown 键
digitalWrite(RST_CTRL, LOW);
Serial.println("RST_CTRL LOW");
}
// 模拟断开CH567
if (c == 'u') {
Serial.println("unplug");
unplug();
}
// 模拟插入CH567
if (c == 'p') {
Serial.println("plug");
plug();
}
}
}
使用方法是通过串口接收指令,代码定义了如下五个动作:
1."d" 按下 PowerDown键
2."f" 抬起 PowerDown键
3."r" 按下然后抬起 RESET键
4."u" 模拟断开 USB 母头设备
5."p" 模拟插入 USB 母头设备