一、项目与功能应用介绍
本项目旨在利用闲置的电脑12V三针散热风扇,通过JDY-31蓝牙模块和微控制器CW32F030C8T6,实现远程无线控制和环境温度检测的功能。除了传统的按键控制外,系统能够支持用户通过笔记本电脑或其他无线设备手动调整风扇转速,或基于环境温度自动启停。这不仅赋予了废弃风扇新的生命,还增加了其智能性和实用性,适用于家庭、办公室等的场景。
二、规定厂商器件介绍
TE Connectivity AMP Connectors 440054-2是一种针座,公插针,接头型连接器。针脚间距2mm,针位数2,额定电流3A,额定电压250V。它用于NTC热敏电阻的连接。将传感器的接口插入该连接器,实现稳定可靠的连接。
Molex 0423751855是一种针座,公插针,分离式连接器。针脚间距2.54mm,针位数2。它作为MCU的reset控制信号输入端口,短接该连接器两个引脚实现reset,节省一个轻触开关,降低系统成本。
三、设计思路与方案介绍
3.1 整体思路
由于风扇散热器的功能不复杂,无需使用高性能处理器。因此主控单元采用CW32F030C8T6微控制器,负责处理传感器数据、生成PWM信号控制风扇、与通信模块交互传输状态参数。由于ESP-01s模块不支持5V供电,增加3.3V降压电路增加系统复杂程度,我选择JDY-31作为通信模块,以“开箱即用”的方式直接与笔记本电脑建立蓝牙通信,无需额外编写代码,使用难度低于ESP-01s Wi-Fi模块。
CW32F030x6/x8 是基于 eFlash 的单芯片微控制器,集成了主频高达 64MHz 的 ARM® Cortex®-M0+ 内核、高速嵌入式存储器(多至 64K 字节 FLASH 和多至 8K 字节 SRAM)以及一系列全面的增强型外设和 I/O 口。所有型号都提供全套的通信接口(三路 UART、两路 SPI 和两路 I2C)、12 位高速 ADC、七组通用和基本定时器以及一组高级控制 PWM 定时器。
NTC热敏电阻型号为MF58 103J3435。
3.2 方案框图
在框图中,MCU的输入信息包括按键状态、NTC电阻阻值(表现为电压);输出为风扇的PWM控制信号、WS2812的SPI控制信号。此外,MCU通过标准的串口通信协议,与JDY-31数据交互,接收上位机下发的指令,并上传风扇状态、温度数值等信息。
鉴于CW32F030C8T6、JDY-31和WS2812均支持5V供电,因此系统采用L7805直接将12V电源降压至5V作为电源输入。为上述器件专门增加一个3.3V LDO是没有任何意义的。
3.3 原理图说明
重要说明:该原理图用于制作实物PCB,其中存在两处错误,包括:U2处的AMS1117-3.3芯片、RTMP1处的连接器电路。图中和附件的PCB源文件都没有修改,原样展示。具体错误信息和修改方法请看后文章节。
散热风扇控制器通过DC-050接口,以12V直流电源供电。支持DC-050接口的12V电源在生活中较常见。同时,控制器预留了一个MOLEX接口(大4Pin接口),编号是CN1。考虑到生活中支持该接口的电源较支持DC-050接口的电源数量少,因此实物中未焊接,未作该接口的功能验证。不焊接CN1连接器,不影响散热风扇的正常工作。MCU的下载接口直接使用普通的4Pin弯排针,因为在使用中不存在频繁下载程序,变更风扇转速的场景。通过3个轻触开关SW1、SW2、SW6,或者蓝牙串口,实现风扇启停、转速增减。为简化电路设计,轻触开关使用软件滤波,不需要焊接100nF贴片电容。
散热风扇的驱动电路设计可进一步精简。在MCU 5V供电的情况下,Q1、Q2两处的三极管可能是多余的。
四、硬件制作与调试
4.1 硬件制作
使用KiCad绘制原理图和PCB。散热风扇系统使用2层PCB板,贴片元器件统一放置于顶层。设计完成后,导出Gerber文件交由捷配打样。随后按照器件从低到高的顺序手工焊接。确定不存在短路现象后,使用Keil编写MCU控制程序,再用调试器下载程序、硬件仿真,确定各模块工作正常后,移除调试器、连接12V风扇。最终使用12V电源验证整套系统的功能。
4.2 硬件调试:系统供电部分
散热风扇原计划使用AMS1117-3.3 LDO为,使用12V电源供电。观察到L7805输出电压5V,但AMS1117的输出电压也是5V,出现问题。经过排查,发现PCB画错了,AMS1117的输出引脚与输入的5V电源直接连接了……
由于CW32F030C8T6 MCU、WS2812灯珠、JDY-31蓝牙模块均支持5V供电,使用5V作为它们的电源,不影响系统的正常运行。因此,在实物PCB上,使用热风枪拆除AMS1117芯片,随后截断图中U2芯片的3、4引脚,并将U2的2、4号引脚直接飞线连接。现在,MCU等元件工作在5V电压下。
4.3 硬件调试:NTC电阻部分
原计划使用电阻分压的方式,通过PA2端口的ADC读取NTC电阻分到的电压来换算出它的阻值。调试中,观察到PA2 ADC读数始终为零。经过排查,发现分压电阻连错了,PA2接口通过NTC和分压电阻直接接地了……
使用1个2kΩ的直插电阻,一端连接PA2,一端连接散热风扇系统的5V电源,恢复分压电路的功能。此时读取的ADC数值恢复正常,使用经验公式可大致换算出环境温度。
4.4 硬件调试:无线模块部分
原计划使用工作电压为3.3V的ESP-01s Wi-Fi串口通信模块。由于3.3V电源被取消,因此换成JDY-31蓝牙模块。JDY-31同样支持串口通信,将待传输的数据直接发送到JDY-31后,会自动借助蓝牙信道传输至电脑,无需额外的人工操作。
模块和PCB的接线方式如下图所示。
4.5 成品效果
图中U2处的飞线请看4.2节说明。
图中的电阻和飞线请看4.3节说明。
4.6 操作方法
使用时,通过蓝牙串口发送“M0”~“M3”分别代表0档(停转)~3档(最大转速),同时WS2812分别显示白色、红色、绿色、蓝色;发送“A1”代表进入自动温控模式,测得温度大于(约)25摄氏度后,风扇自动起转,同时WS2812的颜色动态切换,发送“A0”退出自动温控模式,风扇进入0档(停转),WS2812显示白色。进入自动温控模式后,无法通过蓝牙串口或按键控制风扇档位。
PCB正面的3个轻触开关中,左侧的SW6、中间SW2分别实现风扇档位减少、增加。右侧的SW1控制风扇启停。在风扇启动后,方可通过按键实现转速档位变化。
五、关键代码及说明
5.1 流程图
5.2 按键中断函数
在中断函数内部通过软件延时完成按键动作的硬件滤波,尽管这个操作不合理。随后,判断是否处于自动温控模式,再根据具体的按键更新档位设置,按照最终的档位调整PWM定时器的参数,实现改变PWM占空比的效果。
void GPIOB_IRQHandler(void)
{
delay1ms(20);
/* USER CODE BEGIN */
if(SW_AUTO){
GPIOB_INTFLAG_CLR(bv10|bv2|bv11);
return;
}
if (CW_GPIOB->ISR_f.PIN10)
{
SW4_FSM++;
GPIOB_INTFLAG_CLR(bv10);
}
if (CW_GPIOB->ISR_f.PIN2)
{
SW4_FSM--;
GPIOB_INTFLAG_CLR(bv2);
}
if(SW4_FSM>3) SW4_FSM=3;
else if(SW4_FSM<1) SW4_FSM=1;
if (CW_GPIOB->ISR_f.PIN11)
{
SW3_FSM=!SW3_FSM;
GPIOB_INTFLAG_CLR(bv11);
}
if(SW3_FSM==0){
SW4_FSM=0;
}
switch(SW4_FSM){
case 0: CW_GTIM3->CCR1 = FAN_SPD_0; CW_GTIM3->CCR2 = FAN_SPD_0; break;
case 1: CW_GTIM3->CCR1 = FAN_SPD_1; CW_GTIM3->CCR2 = FAN_SPD_1; break;
case 2: CW_GTIM3->CCR1 = FAN_SPD_2; CW_GTIM3->CCR2 = FAN_SPD_2; break;
case 3: CW_GTIM3->CCR1 = FAN_SPD_3; CW_GTIM3->CCR2 = FAN_SPD_3; break;
default: CW_GTIM3->CCR1 = FAN_SPD_0; CW_GTIM3->CCR2 = FAN_SPD_0; break;
}
/* USER CODE END */
}
5.2 串口中断函数
串口中断函数与按键中断函数的功能类似。需要注意的是,只有通过串口才能控制自动温控模式的开启与关闭。
void UART1_IRQHandler(void)
{
/* USER CODE BEGIN */
uint8_t TxRxBuffer;
uint8_t i;
if(USART_GetITStatus(CW_UART1, USART_IT_RC) != RESET)
{
TxRxBuffer = USART_ReceiveData_8bit(CW_UART1);
USART_SendData_8bit(CW_UART1, TxRxBuffer);
if(TxRxBuffer==0x0A || TxUART1_buffer_idx==7){
if(ReceivedData[0]=='A'){
if(ReceivedData[1]=='1'){
SW_AUTO=1;
}
else{
SW_AUTO=0; SW4_FSM=0;SW3_FSM=0;
}
}
else if(ReceivedData[0]=='L'){
if(ReceivedData[1]=='1') ws2812_enable=1;
else ws2812_enable=0;
}
else if(ReceivedData[0]=='M'){
if(!SW_AUTO){
switch(ReceivedData[1]){
break;
case '0': SW4_FSM=0;SW3_FSM=0; break;
case '1': SW4_FSM=1;SW3_FSM=1; break;
case '2': SW4_FSM=2;SW3_FSM=1; break;
case '3': SW4_FSM=3;SW3_FSM=1; break;
default: break;
}
switch(SW4_FSM){
case 0: CW_GTIM3->CCR1 = FAN_SPD_0; CW_GTIM3->CCR2 = FAN_SPD_0; break;
case 1: CW_GTIM3->CCR1 = FAN_SPD_1; CW_GTIM3->CCR2 = FAN_SPD_1; break;
case 2: CW_GTIM3->CCR1 = FAN_SPD_2; CW_GTIM3->CCR2 = FAN_SPD_2; break;
case 3: CW_GTIM3->CCR1 = FAN_SPD_3; CW_GTIM3->CCR2 = FAN_SPD_3; break;
default: CW_GTIM3->CCR1 = FAN_SPD_0; CW_GTIM3->CCR2 = FAN_SPD_0; break;
}
}
}
TxUART1_buffer_idx=0;
memset(ReceivedData,0x00,8);
}
else{
ReceivedData[TxUART1_buffer_idx++]=TxRxBuffer;
}
USART_ClearITPendingBit(CW_UART1, USART_IT_RC);
}
/* USER CODE END */
}
六、功能展示
具体请看视频。
七、心得体会
在设计和实施这个项目的过程中,我理解了如何将废弃资源转化为有价值的解决方案,强化了我们对微控制器编程、无线通信协议和传感器技术的理解,并且锻炼了嵌入式软硬件调试能力,是一次非常有意义的学习经历。
期待下次的FastBond活动!!