简介:面向STM32单片机开发者和硬件工程师的蓝牙风扇主控板完整设计资料,涵盖AD硬件设计原理图、PCB、封装库及配套软件源码,适合作为学习或产品原型参考。主控采用STM32F030F4P6,搭配TB6612FNG直流驱动、TP4057充电、SX1308升压等电路,2层板双面布局,尺寸50x50mm,整板方案可复现。压缩包共149个文件,包含C/H/S软件源码文件、工程配置文件,以及schdoc/pcbdoc/prjpcb/pcblib等AD设计文件,还附USB充电和升压相关设计,包体约31.79MB。已有1752人学习下载。资料内含完整的原理图、PCB和可编译的软件工程,目录划分清晰,可直接用Altium打开修改,也能快速移植到类似的小家电或便携式风扇产品中,是一份难得的软硬结合参考设计。
1. 一块 50×50 的蓝牙风扇主控板,把充电、升压、驱动和无线全装下
先给一个结论:这个资源的核心不是某个炫技算法,而是把一套锂电池供电的小家电主控板做成可以直接拿去打样的样子。板子尺寸被压在 50×50mm,两层板,双面走线和贴片,MCU 选的是 STM32F030F4P6 的 TSSOP20 封装,仅 20 个引脚却要同时承担蓝牙通信、PWM 调速、充电管理和电压变换协调。正因引脚少,原理图上的电源拓扑必须比常规 STM32 项目更紧凑,这也是拆解时最值得看的地方。
它解决的是「手机控制的小风扇该怎么做」这类实际问题:USB 口进来用 TP4057 给锂电池充电,电池电压经 SX1308 升到 5V,再通过 RT9193 稳压出 3.3V 给 MCU 和蓝牙,电机交给 TB6612FNG 驱动。适合三类人:做小家电硬件选型的工程师,用 Altium Designer(后面统一称 AD)复刻参考板的硬件开发者,以及想把标准外设库下 F030 串口/PWM 代码跑通的学生。
2. 电源链路选型:TP4057、SX1308、RT9193 怎么搭配才不互相干扰
2.1 先理清四个电源域的电流走向
把 USB 充电、电池升压、逻辑供电和电机驱动分开看,整块板的原理图就清晰了。TP4057 只负责 USB 5V 到锂电池的充电路径,典型线性充电芯片,外部只需一个 PROG 电阻设定充电电流。电池输出进入 SX1308,升压后得到稳定的 5V 电机电源轨,这一路电流是四个电源域里波动最大的,电机堵转时瞬时电流可能冲到 1A 以上。5V 再串 RT9193 变成 3.3V,给 STM32F030、蓝牙模块和 TB6612FNG 的逻辑引脚供电。最后 TB6612FNG 的 VM 接 5V,VCC 接 3.3V,输出 PWM 的 PA6 属于 3.3V 域,逻辑阈值能直接兼容 3.3V 方波。
实际设计时要注意:TP4057 的充电电流不要一路顶满,PROG 电阻选 1.2k 到 2k,对应 500~830mA 足够给单节软包电池充电;SX1308 的负载能力受电感饱和电流影响,布局时电感到 SW 引脚的走线要短而粗;RT9193 的输入输出电容要靠近引脚,否则负载突然加大时 3.3V 会有几十毫伏的跌落,蓝牙模块在射频发射瞬间就容易复位。电源顺序理不顺,后面画 PCB 再好看也白搭。
2.2 电源轨分工表
| 电源轨 | 主要器件 | 输入 | 输出 | 承载对象 |
|---|---|---|---|---|
| 充电轨 | TP4057 | USB 5V | 3.7~4.2V 电池 | 单节锂电池 |
| 电机轨 | SX1308 | 电池电压 | 5V | TB6612FNG VM、风扇电机 |
| 逻辑轨 | RT9193 | SX1308 输出 5V | 3.3V | STM32F030、蓝牙模块、TB6612 VCC |
| 地参考 | 电池负端 | 系统 GND | 单点接充电地 | 全部器件 |
SX1308 从电池升压是因为电池电压会从 4.2V 掉到 3.7V 以下,直接驱动标称 5V 的电机转速会随电量下降而变慢,升压后稳压输出才能保证风扇转速平稳。TB6612FNG 的逻辑电源 VCC 接到 RT9193 的 3.3V,而不是直接接 5V,这是很多初学者第一次画这种板会顺手接错的地方。
2.3 TB6612FNG 的选型理由与接口定义
TB6612FNG 比 L298N 更适合这块 50×50 小板。它是双 H 桥,体积极小,单路持续电流在 1A 级别,足够驱动普通 5V 散热风扇;输出级是 MOSFET,不像 L298N 用双极型三极管那样有大压降,同样 100mA 负载下手感冷静得多。对需要电池供电的小家电来说,导通损耗直接决定续航。
| 芯片引脚 | 连接对象 | 说明 |
|---|---|---|
| VM | SX1308 输出 5V | 电机电源,需就近放电容 |
| VCC | RT9193 输出 3.3V | 逻辑电源,和 MCU 同域 |
| GND | 系统地 | 与充电地单点相连 |
| IN1/IN2 | F030 GPIO | 正转、反转、刹车 |
| PWMA | F030 TIM3_CH1 | PWM 调速输入 |
| STBY | 3.3V | 必须拉高,悬空则芯片不工作 |
引脚分配上,IN1/IN2 可以接任意 GPIO,PWMA 必须接能产生 PWM 的定时器引脚。参考板把 PWMA 接在 PA6,因为 STM32F030F4P6 在 TSSOP20 封装里 PA6 刚好引出并映射到 TIM3_CH1,驱动代码只需要配置一个复用引脚就能跑。
2.4 两个关键电阻怎么估算
TP4057 的充电电流由 PROG 到 GND 的电阻决定,SX1308 的输出电压由反馈分压电阻决定。我习惯直接写一段 Python 算,比按计算器快,也能一次性看到多个候选值:
# TP4057: I_CHARGE(mA) = 1000 / R_PROG(kOhm) for r in (1.0, 1.2, 1.5, 2.0): print(f"PROG={r}kOhm -> {1000/r:.0f}mA") # SX1308: VOUT = 0.6V * (1 + R1/R2),R1 是 VOUT 到 FB 的电阻 r2 = 10.0 r1 = 73.2 # 常用 73.2kOhm vout = 0.6 * (1 + r1 / r2) print(f"R1={r1}kOhm, R2={r2}kOhm -> {vout:.2f}V")代码里的 73.2k 和 10k 组合会把输出定在约 5.0V。调参时注意:R_PROG 越大充电电流越小,不是越大充得越快;SX1308 的 R1/R2 选 1% 精度电阻,普通 5% 电阻会让空载电压偏差超过 0.1V,带载后可能触发欠压保护或过压保护。
提示:SX1308 的反馈电阻走线要远离电感,否则开关噪声耦合到 FB 脚,输出会周期性抖动,示波器上看到 1.2MHz 纹波调制就是这个原因。
3. AD 硬件设计复现:TSSOP20 引脚分配、封装核对与双层板布局
3.1 引脚分配:20 脚如何同时放下蓝牙串口、PWM 与方向控制
STM32F030F4P6 是 STM32F0 系列里很入门的一颗,TSSOP20 封装,Flash 16KB,主频默认 48MHz。引脚不够用,就必须把功能映射安排好。参考板的常见做法是:PA9/PA10 给 USART1 接蓝牙模块 TX/RX,PA6 复用为 TIM3_CH1 输出 PWM,PB0/PB1 作为 TB6612FNG 的 IN1/IN2 方向控制,PA2/PA3 预留为调试或呼吸灯。
| 引脚 | 功能 | 去向 |
|---|---|---|
| PA9 | USART1_TX | 蓝牙模块 RX |
| PA10 | USART1_RX | 蓝牙模块 TX |
| PA6 | TIM3_CH1 | TB6612FNG PWMA |
| PB0/PB1 | GPIO 输出 | TB6612FNG IN1/IN2 |
| NRST | 复位 | 10kΩ 上拉到 3.3V |
| BOOT0 | 启动选择 | 10kΩ 下拉到 GND |
| VDD/VDDA | 供电 | 100nF 去耦电容到 GND |
选 PB0/PB1 而不是 PA7/PB13 这些引脚,是为了把高频率信号和 PWM 通道分开。PB0/PB1 只是普通 GPIO,切换方向时即使有毛刺也不会影响 PWM 稳定性。NRST 上拉和 BOOT0 下拉是 F030 最小系统必须的,少一个都可能出现下载正常但复位异常的现象。
3.2 打开 AD 工程先核对封装库再画原理图
原资源是 AD 工程,打开时注意Bluetooth_Fan.uvguix.Administrator是 Keil 的界面布局文件,和 AD 无关;硬件文件要看.SchDoc、.PcbDoc和.PcbLib。先不要急着改原理图,打开 PCB 库,用Ctrl+M测量 TSSOP20 焊盘中心距。F030F4P6 的 TSSOP20 引脚间距是 0.65mm,如果测量时看到 0.635mm 左右,那只是英制单位换算造成的显示误差,不影响使用;但如果焊盘宽度和数据手册里的推荐值偏差超过 0.1mm,就要改封装。
在 AD 里切单位按V再按U,会从 mil 切到 mm。测量时对准两个相邻焊盘的中心点,读数应该是 0.65mm。封装库还需要检查 1 脚位置,TSSOP20 的 1 脚通常有圆点或丝印方框标记,画错会正面贴片反面翻,板子回来全废。
提示:很多网上流传的封装会把焊盘做大 0.1mm 方便手工焊接,做样板可以接受,但量产时必须按数据手册改回,否则立创贴片或者工厂炉后虚焊率会明显上升。
3.3 50×50 双层板的布线顺序与地回路
板子尺寸 50×50,双面布局,布线的顺序建议是:电源网络优先,然后铺地,最后走信号。SX1308 的电感要放在芯片 SW 脚同一侧,顶层走线宽度 15mil 以上,避免大电流路径形成闭合环;TP4057 的充电电流路径用 20mil,因为 USB 充电线一旦偏长,线阻会把电压拉低导致充电电流达不到设定值;RT9193 输出的 3.3V 用 10mil 就够,但去耦电容必须贴近 MCU 的 VDD 引脚。
底层大面积铺 GND,顶层信号间隙也补 GND 铜,这样电机工作时的地弹不会全部压到蓝牙模块的天线参考地上。TB6612FNG 的 VM 和 GND 之间要放 100uF 电解电容和 100nF 陶瓷电容,两个都尽量靠近芯片电源脚;风扇电机线直接焊在板边焊盘上,不要走很长一段细线到接插件,否则电机噪声容易串进蓝牙天线。
3.4 覆铜与 DRC 检查
画完布线后在顶层和底层各铺一块 GND 铜,铺铜区域选择时把蓝牙模块 PCB 天线的正下方排除掉,天线净空区不铺铜,否则天线阻抗会偏移。重新铺铜后跑一次 DRC,检查重点如下:
| 检查项 | AD 菜单 | 常见告警 |
|---|---|---|
| 间距 | Design -> Rules -> Clearance | 小于 0.2mm 报错 |
| 未布线网络 | Design -> Rule Check | Un-Routed Net |
| 短路 | Design -> Rule Check | Short-Circuit |
| 过孔到焊盘 | Design -> Rule Check | Via Touch Pad |
DRC 清零后,再按Tools -> Polygon Pour -> Repour All重新灌一次铜,因为 DRC 检查的是当前铜皮状态,不改过孔和走线其实不需要重铺,但改过之后不重铺就会漏检。到这里硬件部分就可以出一版 Gerber,后面软件调完再回来改板也有基础。
4. 软件源码拆解:F030 标准外设库里的 PWM 调速与蓝牙帧协议
4.1 Keil 工程里的文件先分清责任
打开资源包,Bluetooth_Fan.uvguix.Administrator只是 Keil MDK 的窗口布局,真正编译入口是同目录下的.uvprojx。keilkilll.bat的作用是清理编译产生的临时文件,双击运行后工程不会损坏,下次打开重新编译。stm32f0xx_tim.c、stm32f0xx_usart.c、stm32f0xx_rcc.c、stm32f0xx_can.c、stm32f0xx_i2c.c这些是标准外设库源文件,说明工程没有用 CubeMX 生成的 HAL 代码,而是直接调用标准外设库 API。
标准外设库和 HAL 最大的差别是:调用任何外设函数前,必须先打开相应 RCC 时钟。比如要操作 TIM3,就要先RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE),否则写寄存器全部无效。F030 内部没有外部晶振时使用 HSI 8MHz,经 PLL 倍频到 48MHz 作为系统时钟,启动过程由system_stm32f0xx.c里的SystemInit()完成,应用代码不要去改 PLL 配置,改完串口波特率会整体偏移。
提示:如果复位后蓝牙数据是乱码,优先查
SystemInit()是否把 SYSCLK 稳定在 48MHz,其次查串口波特率寄存器,最后再怀疑模块本身。
4.2 用 TIM3 输出 1kHz PWM 驱动风扇
参考资源里stm32f0xx_tim.c提供了 Timer 驱动函数,我们要做的只是初始化 TIM3 并把占空比映射到比较寄存器。以下配置把 PA6 复用为 TIM3_CH1,输出频率 1kHz,占空比 0% 到 100% 可调:
/* TIM3_CH1 -> PA6 -> TB6612FNG PWMA */ void PWM_Init(void) { GPIO_InitTypeDef gpio; TIM_TimeBaseInitTypeDef tim; TIM_OCInitTypeDef oc; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_1); /* PA6 复用为 TIM3_CH1 */ gpio.GPIO_Pin = GPIO_Pin_6; gpio.GPIO_Mode = GPIO_Mode_AF; gpio.GPIO_OType = GPIO_OType_PP; gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &gpio); /* 48MHz / 48 = 1MHz,1MHz / 1000 = 1kHz */ tim.TIM_Prescaler = 47; tim.TIM_CounterMode = TIM_CounterMode_Up; tim.TIM_Period = 999; TIM_TimeBaseInit(TIM3, &tim); oc.TIM_OCMode = TIM_OCMode_PWM1; oc.TIM_OutputState = TIM_OutputState_Enable; oc.TIM_Pulse = 0; /* 初始占空比 0% */ TIM_OC1Init(TIM3, &oc); TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_Cmd(TIM3, ENABLE); }这段代码的关键参数:TIM_Prescaler = 47表示对 48MHz 做 48 分频,得到 1MHz 计数时钟;TIM_Period = 999表示计数到 1000 个脉冲翻转一次,PWM 频率就是 1kHz。如果电机在 1kHz 下有啸叫,可以把 Prescaler 改成 47、Period 改成 49,得到 20kHz PWM,超出人耳范围。驱动风扇时,20kHz 的开关损耗会比 1kHz 略大,但噪声改善非常明显。
4.3 蓝牙串口帧状态机:从丢帧到稳定接收
手机端发送数据到 HC-05 这类蓝牙模块,MCU 在串口中断里一个字节一个字节收,不能在每个字节到达时就立刻执行电机动作。正确做法是定义一帧短协议,收满后校验通过再处理。参考工程里通常用0xAA + cmd + data + checksum这种固定四字节格式,其中 checksum 是前三字节求和后取低 8 位:
/* 串口接收状态机 */ volatile uint8_t rxState = 0; volatile uint8_t rxFrame[3]; /* cmd, data, checksum */ volatile uint8_t frameReady = 0; void USART1_IRQHandler(void) { uint8_t rx = USART_ReceiveData(USART1); switch (rxState) { case 0: /* 等待帧头 */ if (rx == 0xAA) rxState = 1; break; case 1: /* 收到 cmd */ rxFrame[0] = rx; rxState = 2; break; case 2: /* 收到 data */ rxFrame[1] = rx; rxState = 3; break; case 3: /* 收到 checksum */ rxFrame[2] = rx; if ((0xAA + rxFrame[0] + rxFrame[1]) == rxFrame[2]) frameReady = 1; /* 校验通过,主循环处理 */ rxState = 0; break; default: rxState = 0; break; } }这个状态机的优点是:任何一帧校验失败,状态直接回到 0,下一帧的0xAA就是新帧头,不需要复杂的超时重同步。和 Modbus 这类协议本质上一样,单片机收帧先存缓冲区,校验通过再置标志位,不要在中断里调用TIM_SetCompare1或者GPIO_SetBits,否则中断响应时间不可控,蓝牙模块连续长包时容易丢下一个字节。
4.4 主循环解析命令并更新占空比
主循环处理frameReady标志,根据 cmd 和 data 执行具体的电机控制。发送0xAA 0x01 0x3C 0x07就能把转速设到 60%,因为0x3C是十进制的 60,前三个字节之和0xAA + 0x01 + 0x3C = 0xE7,所以 checksum 填0xE7(代码示例中校验公式会重新计算)。实际实现如下:
/* 主循环中解析帧 */ void ProcessFrame(void) { uint8_t cmd, data; uint32_t compare; if (!frameReady) return; frameReady = 0; cmd = rxFrame[0]; data = rxFrame[1]; if (cmd == 0x01 && data <= 100) /* 设置占空比 */ { compare = (uint32_t)TIM3->ARR * data / 100; TIM_SetCompare1(TIM3, compare); } else if (cmd == 0x02) /* 设置方向 */ { if (data == 1) { GPIO_SetBits(GPIOB, GPIO_Pin_0); /* IN1=1 */ GPIO_ResetBits(GPIOB, GPIO_Pin_1); /* IN2=0 -> 正转 */ } else if (data == 2) { GPIO_ResetBits(GPIOB, GPIO_Pin_0); GPIO_SetBits(GPIOB, GPIO_Pin_1); /* IN1=0, IN2=1 -> 反转 */ } } }参数说明:TIM3->ARR在初始化时是 999,所以 60% 时比较值等于999 * 60 / 100 = 599,PWM 高电平占空比正好 60%。方向控制里 PB0/PB1 对应 TB6612FNG 的 IN1/IN2,两个引脚同时置 1 是刹车,同时置 0 是停止,只有高低电平组合才有正反转。注意 STBY 引脚如果没接 3.3V,方向指令再正确电机也不会转。
5. 合板调试:HC-05 连不上的排查顺序与 AD 改板三个习惯
5.1 HC-05 模块连不上的四个检查点
蓝牙模块调试时最常用的场景就是 HC-05 连不上 STM32,或者手机搜不到设备。先进入 AT 模式:模块上电前按住模块上的小按键,再接电源,状态灯慢闪就表示进入 AT 指令模式。然后在串口调试助手里按字节流发送以下命令,每行结尾加回车换行:
AT AT+NAME=BluetoothFan AT+ROLE=0 AT+PSWD=1234 AT+UART=9600,0,0AT+ROLE=0设置从机模式,手机作为主机去连接;AT+UART=9600,0,0把数据模式波特率固定为 9600,8 位数据,1 位停止位。如果模块连不上,按这个顺序排查:先看供电,很多 HC-05 模块板载 3.3V LDO,VCC 要求接 5V,错接 3.3V 会导致模块工作不稳定但灯却一直在闪;再看串口交叉,STM32_TX 必须接模块 RX,STM32_RX 接模块 TX,两个地必须连一起;然后看波特率,模块配成了 9600,MCU 串口初始化也必须是 9600,否则收到的全是乱码;最后看配对,手机配对时输错 PIN 码,模块会一直拒绝连接,重新上电后按住按键恢复 AT 模式重新配对。
想让手机每次上电都能自动连接蓝牙风扇,不需要在 MCU 里写自动重连代码,把模块设成主模式并固定绑定地址即可:AT+ROLE=1、AT+CMODE=0,然后用AT+BIND=手机蓝牙地址捆绑,模块上电后会主动回连上次绑定的手机。这种硬件端自动回连方案比躺在单片机里维护链路状态简单得多,也稳定得多。
5.2 AD 改板时值得保留的三个习惯
第一,改封装前先备份一份原始.PcbLib,不要直接在原库上改 TSSOP20 的焊盘尺寸。Ctrl+M 测量完后,把偏差记到备注里,对不上原厂数据时从参考板封装改一个新库,而不是改老库,避免后续打开其他原理图全部报错。
第二,改完 PCB 走线后必须按Tools -> Polygon Pour -> Repour All重新覆铜,再按Design -> Rule Check跑 DRC。只改线不重铺铜,DRC 查的是旧铜皮状态,被切断的 GND 网络检查不出来,板子打样回来后可能出现地平面不连续导致的电机噪声。
第三,导出 Gerber 时不要直接点发送,先打开钻孔文件确认孔径是不是和封装库一致。尤其注意 0.3mm 的机械孔在部分板厂需要加价或会改成 0.35mm,封装库里的过孔尺寸如果写错,回来的板子可能装不上 USB 座。把钻孔文件的孔径列和 PCB 封装库里的过孔尺寸逐一对照一遍再发给板厂。
本文还有配套的精品资源,点击获取