Arduino CH376 模块调试指南

去年的时候,介绍过通过串口来和 CH376 通讯【参考1】,最近又将它拿出来玩,和之前不同,这次是和 ESP32 通讯,没想到遇到了奇怪的问题,以此为契机仔细研读 datasheet 总结如下。

  1. 硬件连接:USB 转串口卡,上面的 5V(必须5V),接VCC;GND 接 GND;TX接 RX; RX 接TX; 特别注意,这种板子上有一个5V转3.3V的ASM1117,就是说5V提供给USB 设备,但是芯片是工作在3.3V 下,这种情况下串口一般都能正常工作,但是如果用 SPI 模式,需要特别注意和单片机的电平匹配问题;
  2. 串口发送 CHECK_EXIST (57 AB 06 AA),正确的回复是 0x55 如果没有回复或者是错误的,请检查硬件连接,还有波特率设定;默认情况下波特率为 9600
测试串口参数如图

3.串口发送CMD_SET_USB_MOD (57 AB 15 06), 模式代码为 06H 时切换到已启用的 USB 主机方式,自动产生 SOF 包,正确回答是CMD_RET_SUCCESS 和 USB_INT_CONNECT (0x51 0x15)

  1. 串口发送 DISK_CONNECT(57 AB 30),检查U盘连接,正确回答是 0x14;如果有问题,请更换U盘或者检查格式,在【参考2】给出了一个Bug:如果保留扇区数>255,在老的芯片上会有问题;
  2. 串口发送 DISK_MOUNT (57 AB 31),初始化U盘,并且检测,正确回答是 0x14;
  3. 串口发送 DISK_CAPACITY (57 AB 3E), 查询U盘容量。查询结果需要发送 RD_USB_DATA0查询(57 AB 27),收到返回值 04 FF 7F 7D 00 ,意思是 4个字节,0x7D7FFF= 8224767个扇区电脑上查看如下(感觉这个命令查询的结果是“U盘上最大的扇区号”,所以数量等于这个值加1):

参考:

  1. https://www.lab-z.com/ardch376/
  2. http://www.wch.cn/bbs/thread-63674-1.html 工作人员回复说老版本芯片会有问题

FT232H UART 速度测试

很早之前入手过一块 FT232H 的板卡,这次进行一下 UART 最高速度的测试。测试条件是 Windows 10 操作系统,系统默认的驱动(VCP)。

FT232H 长江智动产的

FT232 上 TXD 是 Pin13 (AD0), RXD 是Pin14(AD1)

FT232H 做 Uart 通讯时的引脚定义
不同功能下引脚的功能

使用串口工具直接发送 HEX 55 55 55 55 55 55 55 55 55 55 值(这样从信号看起来就是不断变化的0 1 信号),示波器查看结果如下:

上面是FT232H uart 12M (波特率  12 000 000)的结果
上面是 6Mhz 的结果

此外,有一些波特率是无法使用的比如:10Mhz ,测试显示无信号输出。测试板子上的晶振是 12Mhz 的,可能是因为这个原因,所有有些分频是无法出来的。从上面可以看出, FT232H UART 模式下,最高的有效传输速度可以达到 12Mhz*(8/10)=9 600 000 bits/s = 1.444Mbytes/s。

本文数据根据实验得出,有问题欢迎朋友直接指出共同探讨。

参考:

1. http://ftdichip.cn/Support/Documents/DataSheets/ICs/DS_FT232H.pdf

D3hot 和 D3Cold

从 Windows8 开始,操作系统将设备的 D3 状态分为2种: D3hot 和 D3Cold。

设备可以直接从 D0 状态进入D3Hot,D3Cold 只能从 D3Hot 状态进入。 从 D0 到 D3Hot 的状态切换是通过驱动程序来完成的。进入 D3Hot 后,仍然能够在这个设备连接的总线(Bus)上看到这个设备。当这个总线上的设备进入 D3Hot 后,总线必须处于 D0 状态。进入 D3Hot 后,设备可以返回 D0 状态,或者进入 D3Cold 状态。

当设备进入 D3Cold 后,总线上无法检测到这个设备(设备完全断电)。当设备进入 D3Cold 后,它所处的总线可以进入低功耗状态。同时这个设备不会响应总线上的检测动作。进入 D3Cold 后,设备只能返回 D0 状态,不能直接切换为 D3Hot 状态。

参考:

1.https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/device-sleeping-states

2.https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/supporting-d3cold-in-a-driver

3.https://blog.csdn.net/qq_38180524/article/details/106079187

4.http://blog.chinaunix.net/uid-7374279-id-5838168.html

WinDbg 查看OS 版本命令:vertarget

5: kd> vertarget
Windows 10 Kernel Version 19041 MP (8 procs) Free x64
Product: WinNt, suite: TerminalServer SingleUserTS
Edition build lab: 19041.1.amd64fre.vb_release.191206-1406
Machine Name:
Kernel base = 0xfffff805`0da00000 PsLoadedModuleList = 0xfffff805`0e62a2b0
Debug session time: Wed Feb 10 15:55:42.009 2021 (UTC + 8:00)
System Uptime: 7 days 23:02:38.960

参考:

1.https://www.cnblogs.com/yilang/p/12009225.html

Kabylake PCH UART 测试工具 UEFI Shell 版

在 KablyLake/AmberLake的PCH上存在着3个UART,默认情况下使用 UART2(第三个UART,INTEL RVP上使用的就是这个Port作为UEFI Debug Log 输出)。为此,编写一个 UEFI Shell Application 能够让三个 UART 分别发送 “This is PCH UART0/1/2”。在通过串口连接出去的机器上可以收到。这样能够方便的确认硬件连接以及配置是否工作正常。

运行结果:

ZUART UEFI Shell 下运行结果

工具下载(无 SourceCode):

ESP32 S2 进一步测试

春节这几天趁着有空再进一步研究了一下 ESP32 S2 的 USB 玩法。线路连接方法和之前介绍的相同【参考1】,特别注意,这次没有连接 ESP32S2 的5V和 USB 端口的5V,  这是防止 USB 端口上的5V和板子上的5V不同导致的电流倒灌。首先测试 HID的例子:

选择 ESP32 Tiny USB 中的 HID 例子

烧写代码后设备管理器中可以看到多出的 HID 设备。

新出现的 ESP32 S2 模拟出来的设备

我手上的开发板是ESP32-S2-Saola-1R,引脚入下图:

ESP32-S2-Saola-1R 引脚图

调试方法:

打开当前 Verbose 模式

这样,可以在 Arduino 串口监视器中看到 Debug 信息,比如插入时有如下信息:

插入时的Debug 信息

接下来研究一下 MSD (MassStorageDevice,U盘)的例子,在Example->ESP32 TinyUSB->MSC。这个例子需要使用1.5MB的 PSRAM ,对于我入手的板子来说 Flash 是4MB ,PSRAM 是2MB,完全能够满足要求。此外,需要 Enable PSRAM,否则会出现不断重启的问题:

需要Enable PSRAM

烧写之后系统中就会出现如下的硬盘。

很小的一个U盘

测试发现最新版本的 ESPTinyUSB 库似乎有问题,下面是我这边正常使用的老版本的库:

参考:

  1. http://www.lab-z.com/esp32s2/ 支持原生USB 的ESP32 :ESP32 S2
  2. https://www.mischianti.org/wp-content/uploads/2020/11/ESP32-S2-Saola-1MI-pinout-mischianti.png

常见 X86 平台TPM 相关缩写介绍

之前看到过很多人用一些词组来证明中文的简洁和高效,比如:人们看到“炮闩”,通过“闩”就能猜到它的用途。这让我想起最近网上的一个笑话:“老师在群里发消息说明天开始一元二次方程。有家长问。能不能一元三次?打个折?” 缺少必要知识,就无法理解“元”的含义。 “一元二次方程” 的英文名是“quadratic equation with one unknown”,从上面大抵能看明白这个方程中有一个未知数以及它的二次方。所以,懂和不懂是取决于知识的储备和语言文字并没有太多关系。

缩写原始词组解释
ACMAuthenticated Code Module用于验证当前密钥之类的模块,放在 BIOS 中的,这个可以看作是 BootGuard 功能的一部分(换句话说,如果想 Enabled BootGuard 功能,除了在  FIT  中 Enable BootGuard,还要保证BIOS中有这个模块。某些情况下,IBV Enabled Debug 功能后,因为 Size 的原因会关闭 ACM ,这样做出来的Debug BIOS 无法在 Fuse 后,并且Enable BootGuard的板子上运行。现象会是:上电之后马上掉电)。
BtGBootGuardBootGuard 在【参考1】有介绍。
BUPBring-Up主板上电过程,有时候指新打出来板子然后Porting BIOS 的过程。
CSEConverged Security Engine系统上的 CSME 系统
dTPMDiscrete TPM分立式TPM。对应有一颗专用芯片,通常使用 SPI 接口和PCH 相连。Intel PTT 相当于将一个 dTPM 集成到SOC 中。
EKEndorsement Key签署密钥,或背书密码。它是一个TPM平台的不可迁移的解密密钥,它是一个2048bit的RSA密钥对。它生成于平台的生产过程中,代表着每个平台的真实身份,每个平台都拥有唯一的一个。在确定平台所有者时,用于解密所有者的授权数据,还有解密与生成AIK相关的数据。签署密钥从不用作数据加密和签名。签署密钥的主要功能是生成身份证明密钥(AIK)和建立TPM平台的所有者,由TPM的所有者来生成存储根密钥SRK,使用SRK来加密、存储其他的密钥。【参考2】
FIPSFederal Information Processing Standard美国联邦信息处理标准
FITFlash Image Tool有时缩写为 FITC, 是一个用于设置 IFWI 的工具,由Intel 提供,跟随 CSME 分发。
FPFField Programmable Fuses可编程熔丝位。在 PCH 中有一些只能编写一次的熔丝位。可以理解成类似保险丝一样的东西,需要的时候通过编程能够将这个位置写死。比如,Enable PTT之后通过设置对应的熔丝位会使得这个功能无法再次 Disabled。
fTPMFirmware TPM固件模拟TPM。通过在 CPU 上运行的代码来模拟实现 TPM 的功能。为了增加安全性,相关代码是放在 Protected Execution Environment (PEE) 来执行的。
IBBInitial Boot Block这是 UEFI 中的概念,用于校验 BIOS 其余部分。
PEEProtected Execution Environment受保护运行环境,能够独立于普通CPU 运行环境来运行代码。  
PRTCProtected Real Time Clock受保护的时钟,能够抵御 Hammering 和 Replay 攻击。对于这两种攻击介绍如下【参考4】   重放攻击(Replay Attacks)又称重播攻击、回放攻击,是指攻击者发送一个目的主机已接收过的包,来达到欺骗系统的目的,主要用于身份认证过程,破坏认证的正确性。重放攻击可以由发起者,也可以由拦截并重发该数据的敌方进行。攻击者利用网络监听或者其他方式盗取认证凭据,之后再把它重新发给认证服务器。重放攻击在任何网络通过程中都可能发生,是计算机世界黑客常用的攻击方式之一。   重放操作 一个电子商务网站,要求客户对电子订单签名以防止非授权用户下订单。攻击者如要冒充某位客户下订单,最好可以获得他的私钥,如果不成功,攻击者可以监听这位顾客的通信,将顾客以前发送的订单记录下来,然后他就可以直接将这些订单发给网站了。因为这些订单的确是合法客户签名过的,如果网站没有一种识别重放订单的机制,它就会不加犹豫地接收这些订单。
PTPPlatform TPM Profile 
RBEROM Boot Extension   
RNGRandom Number Generator随机数生成器
ROTRoot of Trust根信任节点
RPMBReplay Protected Memory Block 防重放攻击内存块, 将PTT数据存放在独立的 NVM 区域。
RPMCReplay Protection Monotonic Counters防重放攻击单调递增计数器。这个计数器只能增加不能减小,通过这样的方式来对抗重放攻击。
RTCReal Time Clock   
RTMRoot of Trust for Measurement   
RTR  Root of Trust for Reporting 
RTSRoot of Trust for Storage   
TCGTrusted Computing Group  可信计算组织。TCG组织制定了TPM(Trusted Platform Module)的标准,很多安全芯片都是符合这个规范的。而且由于其硬件实现安全防护,正逐渐成为PC,尤其是便携式PC的标准配置。
TPMTrusted Platform Module可信平台模块。
VSCVirtual Smart Cards 虚拟智能卡。常见的公交卡就是一种智能卡。可以看到它能够实现身份验证,支付等等的功能。同样的,虚拟智能卡也能实现这样的功能。可以想象下面的应用场景:通过 TPM 实现了 Virtual Smart Cards 的功能,然后 Windows 就可以访问企业内部的敏感资源。比如,你将一份文档发送到企业内部打印机上,之前需要刷胸卡来完成打印,现在有了 VSC 可以通在打印机上刷笔记本电脑的方式完成打印。

可以看到,上面的很多词,如果不是专业人员,无论是英文还是中文都是需要多加解释的。再比如:“有一个机械中特别重要的装置,叫活塞,英文是piston。不过在中国,这个词最早不叫活塞,而叫“鞲鞴”。来,眼睛别晕,先扶着墙把晚饭吐出来,然后把这个字复制到word里,放大三倍字号。这个词,念“勾背”。据李文、戴吾三两位考证,是中国第一艘火轮船的制造者徐寿发明的,徐寿1871年在《汽机发轫》首次把piston翻成鞲鞴。别看这词冷僻古怪又麻烦,还真是有典故的。“鞲”字意义是革制的皮套,引申成皮制的鼓风机,也就是风箱;“鞴”字就更精妙了,它的意思是:水受压而喷涌而出。唐代皮日休的《通玄子栖宾亭记》:“源内橐籥鞴出琉璃液。”一鼓一压,正是活塞工作之象。徐寿文化水平太高,用这两个字来译piston,真是用典贴切,古今妙译里,也排得上号,就是他妈太麻烦了……所以后来大家普遍都使用“活塞”这个更浅显的词,鞲鞴则只留存在专业领域。”【参考3】

参考:

1.https://www.lab-z.com/btg/ Boot Guard 简介

2.https://www.cnblogs.com/embedded-linux/p/6716740.html TPM Key相关概念

3.https://www.zhihu.com/question/24449484/answer/27850454 作者:马伯庸

4.https://baike.baidu.com/item/%E9%87%8D%E6%94%BE%E6%94%BB%E5%87%BB 重放攻击

5.https://docs.microsoft.com/en-us/windows/security/identity-protection/virtual-smart-cards/virtual-smart-card-overview

6.https://baike.baidu.com/item/%E6%99%BA%E8%83%BD%E5%8D%A1 智能卡

智能卡(Smart Card) :内嵌有微芯片的塑料卡(通常是一张信用卡的大小)的通称。一些智能卡包含一个微电子芯片,智能卡需要通过读写器进行数据交互。智能卡配备有CPU、RAM和I/O,可自行处理数量较多的数据而不会干扰到主机CPU的工作。智能卡还可过滤错误的数据,以减轻主机CPU的负担。适应于端口数目较多且通信速度需求较快的场合。 卡内的集成电路包括中央处理器CPU、可编程只读存储器EEPROM、随机存储器RAM和固化在只读存储器ROM中的卡内操作系统COS(Chip Operating System)。卡中数据分为外部读取和内部处理部分。

智能卡(Smart Card) :内嵌有微芯片的塑料卡(通常是一张信用卡的大小)的通称。一些智能卡包含一个微电子芯片,智能卡需要通过读写器进行数据交互。智能卡配备有CPU、RAM和I/O,可自行处理数量较多的数据而不会干扰到主机CPU的工作。智能卡还可过滤错误的数据,以减轻主机CPU的负担。适应于端口数目较多且通信速度需求较快的场合。 卡内的集成电路包括中央处理器CPU、可编程只读存储器EEPROM、随机存储器RAM和固化在只读存储器ROM中的卡内操作系统COS(Chip Operating System)。卡中数据分为外部读取和内部处理部分。

Step to UEFI (231)直接修改 ROM 中的版本号

某些情况下,我们需要在没有源代码的情况下需要对BIOS ROM 进行直接修改。本文就以修改 OVMF.FD 为例介绍这个过程。

首先介绍一下这次修改的目标,使用下面的命令生成 Debug Log:

qemu-system-x86_64 -bios "ovmf.fd" -debugcon file:debug.log -global isa-debugcon.iobase=0x402

其中有下面这样一个错误:

SecCoreStartupWithStack(0xFFFCC000, 0x820000)
LABZ report checksum error!
Register PPI Notify: DCD0BE23-9586-40F4-B643-06522CED4EDE
Install PPI: 8C8CE578-8A3D-4F1C-9935-896185C32DD3
Install PPI: 5473C07A-3DCB-4DCA-BD6F-1E9689E7349A
The 0th FV start address is 0x00000820000, size is 0x000E0000, handle is 0x820000
Register PPI Notify: 49EDB1C1-BF21-4761-BB12-EB0031AABB39
Register PPI Notify: EA7CA24B-DED5-4DAD-A389-BF827E8F9B38
Install PPI: B9E0ABFE-5979-4914-977F-6DEE78C278A6

我们的目标就是通过修改 OVMF.FD 来去掉这个错误。

使用 UEFITool 打开 OVMF.FD ,使用搜索功能查找错误字符串,可以看到是位于 SecMain 中。这个文件没有压缩,对于我们搜索来说会很方便。

为了研究代码,还需要使用这个工具将SecMain 释放出来:

释放出来的文件是以  “MZ”开头的:

 使用 IDA 来分析解压出来的这个文件,特别注意,因为代码中含有64Bit的指令,必须使用 IDA-64 才能得到结果。

我们需要找到相关的代码,在菜单中选择 View->Open subviews->Strings

先用Ctrl+F 找到前面提到的字符串,在字符串上双击即可跳转到对应的代码:

新版本的 IDA 默认使用图形化反编译结果,看起来并不是很直接,右键切换到 Text View

这里就可以看的很清楚,比较 rax 和 rdx ,如果不相等就输出字符串,如果相等就跳过:

使用 Hex View 查看,74 0F就是 jz short loc_FFFCCE37 这个跳转语句,将这条语句修改为 jmp short loc_FFFCCE37 就不会输出错误提示了。

修改方法是:在 OVMF中搜索 FD FF FF 48 3B C2 74 0F (整个文件中只有一处),找到后将 74 修改为改成 EB (JMP)。这样修改后再次使用 QEMU 启动修改后的 OVMF.FD ,可以在 Debug.log 中看到之前的字符串已经消失。

本文使用的,修改之前的 OVMF 可以在这里下载:

这个信息是在 Omvf/Sec/SecMain.c 中加入如下代码生成的:

//
  // Find PEI Core entry point
  //
  Status = PeCoffLoaderGetEntryPoint ((VOID *) (UINTN) PeiCoreImageBase, (VOID**) PeiCoreEntryPoint);
  if (EFI_ERROR (Status)) {
    *PeiCoreEntryPoint = 0;
  }

//LABZ_Debug_Start
	if (((EFI_PHYSICAL_ADDRESS) FindImageBase -(EFI_PHYSICAL_ADDRESS) FindAndReportEntryPoints)!=0x0) {
		DEBUG ((EFI_D_ERROR, "LABZ report checksum error!\n"));
	}
//LABZ_Debug_End

  return;
}

做一个 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.5×3.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 中使用下面这个地址:

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