基于 CH554 实现一个小夜灯。
当下的小夜灯普遍存在着痛点:
1.待机时间短
2.颜色不可调,夜间太亮光线刺眼
3.点亮时间不可调,不方便使用
为此,制作了这样一个小夜灯:使用 18650 电池,同时外壳设计上预留了最够的空间,可以根据用户需要自行扩展加大电池通量。颜色和点亮时间可以用过串口自行设置。
核心部件有2个,一个是 HC-SR602 人体红外感应模块;另外一个是CH554 单片机芯片。此外,外部还有TP4056充电模块,18650电池,XT1861B502MR-G升压芯片,5V开关芯片和SN74AHC1G32DBVR或门芯片。
基本原理是 18650和TP4056充电模块配合工作,负责充放电管理。TP4056充电模块自带一个TypeC接口可以用于充电。当18650放电到2.4V时,TP4056充电模块自动停止工作防止过放。然后XT1861芯片负责将2.4-4.2V电压升压到5V 提供给HC-SR602 人体红外感应模块使用。当这个有人触发红外感应模块后,模块输出到或门芯片,经过运算后用于触发SY6280AAC进行供电。之后,CH554 根据存储的颜色控制 WS2812 LED 发光。同时根据设定的时间控制前面提到的或门。这样就可以实现即便人体红外感应模块输出停止工作之后,仍然输出5V。
HC-SR602模块主要参数(在底板上)
- 工作电压:3.3V-15V;
- 静态电流:20uA;
- 感应距离:最大5M;建议0-3.5M;
- 信号电平输出:H=3.3V(检测到周围有人体);L=0V(检测周围无人体);
XT1861产品特点(在底板上)
· 最高效率:94%
· 最高工作频率:300KHz
· 低静态电流:15µA
· 输出电压:1.8V~5.0V(步进 0.1V)
· 输入电压:0.9V~6.5V
· 低纹波,低噪声 小体积封装
这里设计的是主控部分,如果想整体工作起来需要配合底板。具体项目在 https://oshwhub.com/zoologist/ch554-xiao-ye-deng-20250510
这里主控部分完整的主要功能是:
1.接收来自串口的,LED 颜色和时长的设定;
2.工作之后负责控制LED 颜色
电路图:

PCB 设计:

代码使用 Arduino 完成:
#ifndef USER_USB_RAM
#error "This example needs to be compiled with a USER USB setting"
#endif
#include "src/userUsbCdc/USBCDC.h"
#include
#include "DataFlash.H"
#include "include/ch5xx.h"
#define NUM_LEDS 2
#define COLOR_PER_LEDS 3
#define NUM_BYTES (NUM_LEDS*COLOR_PER_LEDS)
__xdata uint8_t ledData[NUM_BYTES];
#define BIT1 2
// USB 串口 Buffer
uint8_t recvStr[6];
uint8_t recvStrPtr = 0;
// 之前保存的颜色值
uint8_t rValue, gValue, bValue;
uint16_t TimeLighting;
// 定义电源控制引脚
#define POWERCTRL 15
// 定义LED信号线
#define LEDCOLOR 14
#define NEOPIXELSHOW neopixel_show_P1_4
unsigned long ElspLighten = 0;
unsigned long Elsp = 0;
void SetLEDColor(uint8_t r, uint8_t g, uint8_t b) {
for (uint8_t i = 0; i {
set_pixel_for_GRB_LED(ledData, i, r,g,b);
NEOPIXELSHOW(ledData, NUM_BYTES);
delay(10);
}
}
void setup() {
// 供电引脚接管电源
pinMode(POWERCTRL, OUTPUT);
digitalWrite(POWERCTRL, HIGH);
// LED 颜色控制
pinMode(LEDCOLOR, OUTPUT);
USBInit();
// 读取颜色信息
Flash_Op_Check_Byte1 = 0x00;
Flash_Op_Check_Byte2 = 0x00;
ReadDataFlash(0, 1, &rValue);
ReadDataFlash(1, 1, &gValue);
ReadDataFlash(2, 1, &bValue);
// 读取时长
ReadDataFlash(3, 2, &TimeLighting);
// 这里需要写成这样,避免上电亮一下的问题
delay(10);
//set_pixel_for_GRB_LED(ledData, 0, 0, 0, 0);
//NEOPIXELSHOW(ledData, NUM_BYTES);
SetLEDColor(0,0,0);
delay(10);
// 读取之前保存的灯颜色
//set_pixel_for_GRB_LED(ledData, 0, rValue, gValue, bValue);
//NEOPIXELSHOW(ledData, NUM_BYTES);
SetLEDColor(rValue, gValue, bValue);
delay(100);
ElspLighten = millis();
}
void Enter_DeepSleep(void)
{
// 第一步:关闭所有外设模块
SAFE_MOD = 0x55; // 进入安全模式
SAFE_MOD = 0xAA; // 解锁寄存器写保护
PCON &= ~BIT1; // 确保PD位初始为0
IE_EX = 0x00; // 关闭扩展中断
IE = 0x00; // 关闭所有中断
TCON = 0x00; // 关闭定时器控制
TMOD = 0x00; // 关闭定时器模式
SAFE_MOD = 0x00; // 恢复安全模式
// 第二步:设置IO口为低功耗状态
P1_DIR_PU = 0x00; // 所有IO设为输入模式
P3_DIR_PU = 0x00; // 所有IO设为输入模式
// 第三步:进入停机模式
SAFE_MOD = 0x55; // 二次确认安全模式
SAFE_MOD = 0xAA;
PCON |= BIT1; // 置位PD位进入停机模式
PCON |= BIT1; // 推荐重复写入确保执行
while (1);
}
void loop() {
while (USBSerial_available()) {
char serialChar = USBSerial_read();
recvStr[recvStrPtr++] = serialChar;
if (recvStrPtr == 5) {
// 测试命令
if ((recvStr[0] == 0x55) && (recvStr[1] == 0xCC)) {
USBSerial_print(rValue);
USBSerial_flush();
USBSerial_print(gValue);
USBSerial_flush();
USBSerial_print(bValue);
USBSerial_flush();
USBSerial_println(TimeLighting);
USBSerial_flush();
}
// 设置颜色
if ((recvStr[0] == 0x55) && (recvStr[1] == 0xAA)) {
// 记录收到的颜色信息
rValue = recvStr[2];
gValue = recvStr[3];
bValue = recvStr[4];
// 将颜色信息写入 eeprom
Flash_Op_Check_Byte1 = DEF_FLASH_OP_CHECK1;
Flash_Op_Check_Byte2 = DEF_FLASH_OP_CHECK2;
uint8_t result = WriteDataFlash(0, &recvStr[2], 3);
if (result == 0) {
// 写入成功
USBSerial_println(result);
USBSerial_flush();
} else {
// 写入失败
USBSerial_println(result);
USBSerial_println("f1");
USBSerial_flush();
}
//set_pixel_for_GRB_LED(ledData, 0, rValue, gValue, bValue);
//NEOPIXELSHOW(ledData, NUM_BYTES);
SetLEDColor(rValue, gValue, bValue);
}
// 设定时长的命令
if ((recvStr[0] == 0x55) && (recvStr[1] == 0xBB)) {
// 记录收到的颜色信息
TimeLighting = (recvStr[2]) + (recvStr[3] 200) {
recvStrPtr = 0;
Elsp = millis();
}
// 到达点亮的时间后关闭,如果是插在电脑上则不关闭
if ((millis() - ElspLighten > TimeLighting * 1000UL) && (USBConfiged == 0)) {
// 关灯
//set_pixel_for_GRB_LED(ledData, 0, 0, 0, 0);
//NEOPIXELSHOW(ledData, NUM_BYTES);
SetLEDColor(0, 0, 0);
digitalWrite(POWERCTRL, LOW);
// 进入省电模式
Enter_DeepSleep();
}
}
焊接后的实物:

安装后的照片


3D外壳设计图:
完整代码:
完整电路图和PCB:
工作的测试视频