做一个 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\

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

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

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

发表评论

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