做一个 USB 提醒器

对于现在的电脑来说,USB是外部通讯的不二选择,通过这个接口可以引出键盘鼠标等等外部设备。这次尝试使用国产的 CH55X 系列单片机来做一个 USB 提醒器,通过这个设备能够做到LED 发光提示还有蜂鸣器声音提示。

首先介绍一下 CH55X 系列芯片是南京沁恒(WCH)出品的带有USB功能的系列单片机【参考1】,提起这家公司的名称大多数人会感到陌生,但是如果提起CH340/CH341芯片大家都会非常熟悉,这款低成本的USB串口转换芯片就是这家公司的产品。这次设计用到的 CH552 和 CH551/CH554 是同一个系列。他们主要的差别是:CH551 是最低端的,工作频率是 24Mhz,FLASH 有 10K (可以看作是单片机的 ROM),内存是 512+256字节,只能做 USB Device;CH552比前者配置稍微高了一些,FLASH空间有16K,内存有1K+256字节,只能做 USB Device;CH554比前面都高级,FLASH 和 内存和 CH552 相同,但是可以当作 USB Host 使用能够实现解析USB 键盘鼠标这样设备的工作。可以看出,相比 Atmel 的 32U4 (Arduino Leonardo),主要缺点是内存太小。但是胜在价格便宜,引脚简单(这样容易焊接)。在立创商城上面的芯片价格如下(CH552/4 不同封装引脚数量不同,所以价格是在一个范围内)

CH551G   2.475元

CH552      2.7-2.78元

Ch554      5.43-7.03元

32u4        23.51元

因此,如果CH55X 系列芯片能够满足你的需求,能够大大降低成品的成本。同时, CH55X 系列单片机对于外围元件要求极低,无须晶振,只要合适的贴片电容即可工作起来。

这次设计使用CH552G芯片是 SOP16 封装,同样的 CH554 也有SOP16封装可以直接替换(CO-Layout)

电路设计如下:

主控部分

这个芯片使用C1/C2 两个 0.1uF 的电容即可工作,5V 供电,从 VCC/VDD 引脚进入。上图中 V33 经过 R1 (10K)连接到 USB+引脚上,其中的 PAD1无须上键在这里作为开关使用用于控制芯片进入 Bootloader Mode。

板子上还有一个 WS2812B MINI(3.5x3.5mm) 用于提供灯光提示,同样的这个元件无须外围配合,直接用CH55X P1.5即可控制:

外围有一个蜂鸣器,因为CH55X 引脚提供电流有限,所以使用S8050这个三极管扩流,同时外加R3用于限流,主要是避免声音太大。CH55X  P3.4 Pin 用于控制蜂鸣器开关。因为该引脚是 PWM 引脚,因此这里即可以使用有源蜂鸣器和无源蜂鸣器。有源蜂鸣器的含义是“内部有震荡源”,类似于电动机,有了供电就能发声;与其相反,无源蜂鸣器含义是“内部没有震荡源”,因此需要用引脚电流变换来驱动它。对于有源蜂鸣器,P3.4 当作普通的GPIO 即可;对于无源蜂鸣器,需要设定为 PWM 然后给他设置占空比和频率(默认即可)。

PCB 设计如下:

切换为 3D 模式预览如下(这次设计的 Symbol使用的都是嘉立创的,所以自带了 3D 模型,非常直观):

正面

背面

拿到手的 PCB和焊接后的照片如下:

接下来就开始软件设计了。这个芯片原本设计使用 C51 来进行变成,用户可以通过 Keil 这样的集成开发环境进行编程,但是对于我们来说C51还是过于复杂。这里使用 Github上的一个开源项目:ch55xduino【参考2】,能够让我们像开发 Arduino 一样为 CH551/2/4 进行开发。

首先介绍一下项目的安装:和其他的所有的第三方板卡一样, 在 Preferences --> Additional Boards Manager URLs 中加入这个板卡的地址https://raw.githubusercontent.com/DeqingSun/ch55xduino/ch55xduino/package_ch55xduino_mcs51_index.json

之后打开 Boards Manager

搜索这个项目

Install 安装之后在 Boards Manager 中能够看到这个板卡,编译时,CH551使用 CH551 Board, CH552/4 使用 CH552 Board

接下来就是如何编译一个程序,可以在 Example 中看到专用的例子,比如下面就是关于 USB 设备的例子:

打开一个例子尝试编译,完成后就需要做上传的准备工作了。刚拿到手 ,板子上没有Bootloader,需要短接板子背面 PAD 处(电路图中是0.1uF电容,实际上不上件预留为空),这样V33会通过10K 电阻后进入 D+ Pin,板子会进入 Bootloader Mode。

属性如下:

使用 Zadig 给他安装一个驱动,勾选 Edit

重命名为 USB Module

安装之后:

编译后会自动搜索名为 “USB Module”的设备并且上传

需要注意的是:如果你的代码没有实现 USB 串口,那么只能短接PAD 让板子进入 BootLoader Mode再次上传。

了解了上面的知识,即可着手设计代码实现提醒器的功能。我们设计了2种提醒方式:LED 和蜂鸣器,分别通过2个命令进行设置,形式如下:

  1. [cRGB]  R/G/B 分别是红绿蓝的取值,例如,命令 [c123] (ASCII)  5B 63 31 32 33 5D (十六进制)会将 LED RGB 分别设置为 0x31 0x32 0x33;命令 5B 63 FF 00 00 5D (十六进制)会设置为全红;
  2. [bThighTlow]  Vhigh和 Vlow 分别是设置的时间高位和地位,例如, 命令 [b12] (ASCII)  5B 62 31 32 5D (十六进制)将会设置蜂鸣器在 0x32 * 0x10 +0x32=0x342 (834秒)后响起来;命令5B 62 01 00 5D (十六进制)将会设置蜂鸣器在 0x01 * 0x100 +0x00=0x100 (256秒)后响起来;可以看出,可以设置最大 0xFFFF (65535秒)后响起来。

完整代码如下:

#define BEEPERPIN 34
#define LEDPIN 15
        
#define TX(LedColor) {\
                  if (((LedColor)&0x80)==0) {\
                       XdigitalWriteFast(1,5,HIGH);\
                       (LedColor)=(LedColor)<<1;\
                       XdigitalWriteFast(1,5,LOW);\
                       XdigitalWriteFast(1,5,LOW);\
                       XdigitalWriteFast(1,5,LOW);\
                   }else{\
                       XdigitalWriteFast(1,5,HIGH);\
                       XdigitalWriteFast(1,5,HIGH);\
                       XdigitalWriteFast(1,5,HIGH);\
                       XdigitalWriteFast(1,5,HIGH);\
                       XdigitalWriteFast(1,5,HIGH);\
                       XdigitalWriteFast(1,5,HIGH);\
                       XdigitalWriteFast(1,5,HIGH);\                       
                       (LedColor)=(LedColor)<<1;\
                       XdigitalWriteFast(1,5,LOW);\
                       XdigitalWriteFast(1,5,LOW);\
                       XdigitalWriteFast(1,5,LOW);\
                       XdigitalWriteFast(1,5,LOW);\
                       XdigitalWriteFast(1,5,LOW);\
                       }\
                  }

#ifndef USER_USB_RAM
#error "This example needs to be compiled with a USER USB setting"
#endif

#include "src/userUsbCdc/USBCDC.h"

void setup() {
  // 设置蜂鸣器Pin 为Low (停止发声)
  pinMode(BEEPERPIN,OUTPUT);
  digitalWrite(BEEPERPIN,LOW);
  // 设置LED 控制Pin
  pinMode(LEDPIN,OUTPUT);
  digitalWrite(LEDPIN,LOW);  
  USBInit();
}

byte Status=0;
byte RValue,GValue,BValue;
byte THigh=0,TLow=0;
unsigned long CounterDown=0xFFFFFFFF;

void loop() {
  while (USBSerial_available()) {
    // 接收来自USB串口的字符
    char serialChar = USBSerial_read();
  if ((serialChar == '[')&&(Status==0)) { Status=1; continue;}
      if ((serialChar == 'c')&&(Status==1)) {Status=2; continue;}
      if (Status==2) {RValue=serialChar; Status=3; continue;}
      if (Status==3) {GValue=serialChar; Status=4; continue;}
      if (Status==4) {BValue=serialChar; Status=5; continue;}
      if (Status==5) {
          if (serialChar == ']') {Status=6;continue;}
          else {Status=0; continue;}
      }
      if ((serialChar == 'b')&&(Status==1)) {Status=7; continue;}
      if (Status==7) {THigh=serialChar; Status=8; continue;}
      if (Status==8) {TLow=serialChar; Status=9; continue;}
      if (Status==9) {
          if (serialChar == ']') {Status=10; continue;}
          else {
                // 如果最后收到的字符不是 [ 那么需要重新开始
                Status=0; continue;
          }
      }
  } 

  // 根据收到的 cRGB 设置 LED 颜色
  if (Status==6) {

    USBSerial_println_s("RGB");
    USBSerial_println_i(RValue);
    USBSerial_println_i(GValue);
    USBSerial_println_i(BValue);
    USBSerial_flush();

    // 设置 WS2812 颜色, 特别注意这里和时许非常相关,具体数值都是示波器测量取得
    //Send Green value from Bit7 to 0
    TX(GValue);TX(GValue);TX(GValue);TX(GValue);TX(GValue);TX(GValue);TX(GValue);TX(GValue);
    //Send Red value from Bit7 to 0
    TX(RValue);TX(RValue);TX(RValue);TX(RValue);TX(RValue);TX(RValue);TX(RValue);TX(RValue);
    //Send Blue value from Bit7 to 0
    TX(BValue);TX(BValue);TX(BValue);TX(BValue);TX(BValue);TX(BValue);TX(BValue);TX(BValue);  
    
   // 重新设置为状态 0
    Status=0;
  }
  
  // 根据设置的延时开始倒计时
  if (Status==10) {
    if ((THigh==0)&&(TLow==0)) {
        digitalWrite(BEEPERPIN,LOW);
      }
        // 设置触发时间
    else CounterDown=(THigh*256+TLow)*1000UL+millis();
    Status=0;
  }

   // 如果当前处于状态 0 并且设置的触发时间小于当前时间,那么就开始响
   if ((Status==0)&&(CounterDown<millis())) {
      // 对应 Pin 拉高,蜂鸣器开始发声
      digitalWrite(BEEPERPIN,HIGH);
      // 拉高之后会一直发声
      CounterDown=0xFFFFFFFF;
   }
}

特别提一句:WS2812 有很多版本,彼此之间在时序上有差别。比如,下面两种从Datasheet看就是有差别的,前者的代码(CH55xDuino)在后者(这次设计使用的)上会有问题:

WS2812D-F8WS2812B-Mini
T0H400±150ns300±80ns
T0L850±150ns790±210ns
T1H850±150ns790±210ns
T1L400±150ns300±80ns
RES>50us>280us

所以,我在代码上重写了 WS2812 相关部分,因为对时序要求很高,所以最好使用汇编语言,但是因为我对 C51汇编很陌生,于是只能写成宏的方式。有兴趣的朋友可以尝试修改CH55xduino的库文件。

BOM 如下,不包括PCB 在10元左右,可以看到这个芯片在制作 USB 相关设备方面很有竞争力:

虽然CH55X系列芯片有诸多好处,但是还存在如下缺点:

  1. 进入 BootLoader后,在 USB 3.0 的 USB口上经常会出现 Yellow Bang 的情况。可以尝试 Disable再Enable 看看能否消除之。如果始终不行,推荐将板子使用一个 USB2.0 HUB和 USB 3.0 相连,这个应该时芯片本身的 Bug(作为 USB 普通设备,不会遇到这个问题);如果一直存在这个问题,那么可以先卸载,然后再重新安装驱动试试;
  2. 淘宝有很多CH552 开发板,强烈建议在动手实验之前入手一个作为开发板,上面会有按钮,按下后插入USB端口直接进入 BootLoader模式,方便调试;
  3. 淘宝购买后,拿到手请及时检查芯片,我在Taobao 购买的 CH554 开发板拿到手几个月后再用时发现上面竟然是 CH552的芯片,因为他们封装相同,所以当时没有注意,再去找售后时发现店铺竟然已经关门。

参考:

  1. http://www.wch.cn/products/category/5.html 单片机系列芯片选项指南
  2. https://github.com/DeqingSun/ch55xduino
  3. https://atta.szlcsc.com/upload/public/pdf/source/20190620/C114583_ECCAEB884D1CBA01F5696FFC90F90D84.pdf WS2812B-Mini DataSheet
  4. https://item.szlcsc.com/150447.html WS2812D-F8 DataSheet\

========================================

因为 GitHub 在访问上可能存在的问题,所以我在这里放一个 json 文件,就是说你可以在 Preferences --> Additional Boards Manager URLs 中使用下面这个地址:

https://www.lab-z.com/wp-content/uploads/2021/05/package_ch55xduino_mcs51_index.json

《做一个 USB 提醒器》有6个想法

  1. ------------------------------------------------------------------
    CH55x Programmer by VNPro
    ------------------------------------------------------------------
    Load file as hex
    Loaded 7338 bytes between: 0000 to 1CD7
    Found no CH55x USB
    上传项目出错

    USB Moudle 设备也正常。就是上传失败。

    1. VNPro is compatible with libUSB driver. So you can use Zadig to use the libUSB driver for the boot loader mode instead of WinUSB. From my testing, it is a lot more reliable than WinUSB on Windows

        1. You are most welcome 🙂

          I have been trying to solve the "Yellow Bang" issue you mentioned when using the Windows WinUSB driver. Using your suggested method of a USB hub does help in one Windows10 Laptop (The others just have a success rate of < 20%). So just would like to check with you if you have any success on that matter. Thanks.

          I have tried to reach out to the WCH company (technical support), they are not being too helpful 🙁

          1. 嗯,我也联系过 WCH, 后来发现他们家的 Driver 不会有这样的问题,然后对方也没有什么办法了。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注