这次我们来看一个基于 STM32F103C8 微控制器和 Proteus 仿真软件实现的“无叶风扇台灯”项目。这个项目将两种常见的家用电器功能——风扇和照明——集成在一个虚拟设备中,并通过 OLED 显示屏和蓝牙模块实现状态显示与远程控制。对于学习嵌入式开发、单片机编程以及电路仿真的朋友来说,这是一个非常典型的综合实践案例。
项目的核心在于利用 Proteus 这一强大的电子设计自动化软件,在电脑上搭建一个完整的虚拟硬件系统,包括 MCU、外围驱动电路、显示模块和通信模块。你不需要购买任何实体元器件,就能完成从电路设计、程序编写到功能调试的全过程。这极大地降低了学习门槛和试错成本,尤其适合在校学生和嵌入式初学者进行项目演练。
本文将带你从零开始,完成这个仿真项目的环境搭建、电路绘制、程序编写与功能测试。你会了解到如何配置 STM32 的 GPIO、PWM、I2C 和 USART 等外设,如何驱动 OLED 显示状态信息,以及如何通过蓝牙实现手机 APP 对风扇和灯光的控制。整个过程完全在软件中完成,但涉及的开发思路和调试方法与真实硬件项目高度一致。
1. 核心能力速览
| 能力项 | 说明 |
|---|---|
| 核心控制器 | STM32F103C8(ARM Cortex-M3 内核) |
| 仿真平台 | Proteus 8 Professional(或更高版本) |
| 开发环境 | Keil uVision 5 / STM32CubeIDE |
| 主要功能 | 1. 无叶风扇模拟(多档风速控制) 2. LED 台灯模拟(多档亮度调节) 3. OLED 显示当前工作模式与参数 4. 蓝牙远程控制(开关、档位切换) |
| 关键外设 | GPIO(按键输入、LED输出)、PWM(风扇/灯光驱动)、I2C(OLED)、USART(蓝牙) |
| 硬件门槛 | 仅需一台普通电脑,无需实体开发板与元器件 |
| 适合场景 | 嵌入式系统学习、单片机课程设计、Proteus 仿真练习、物联网控制原型验证 |
2. 适用场景与使用边界
这个仿真项目主要适用于以下几类人群和场景:
- 嵌入式初学者:希望通过一个完整项目学习 STM32 外设(GPIO、定时器、I2C、串口)的综合应用。
- 高校学生:用于完成单片机原理、嵌入式系统等课程的课程设计或毕业设计,提供完整的仿真方案和代码参考。
- 电子爱好者:在动手焊接实物前,先用仿真验证电路设计和程序逻辑的可行性,避免硬件损坏。
- 物联网原型验证:快速验证“设备状态显示(OLED)+ 无线控制(蓝牙)”这一经典物联网终端架构。
使用边界与注意事项:
- 仿真与实物的差异:Proteus 仿真模型是理想化的,其时序、电气特性与真实元器件存在差异。仿真成功不保证实物一次成功,但能排除大部分逻辑错误。
- 性能极限测试:仿真难以模拟复杂的电源噪声、电磁干扰以及 MCU 在高负载下的真实发热情况。
- 外设模型限制:Proteus 的某些高级外设模型或特定型号芯片可能不支持,需要确认元件库是否包含所需模型(如特定的蓝牙模块型号)。
- 仅供学习研究:本项目仿真的是一个概念产品,其电路和控制逻辑为教学目的设计,不涉及真实的空气动力学和光学设计。
3. 环境准备与前置条件
在开始之前,请确保你的电脑已安装以下软件,这是项目运行的基础。
Proteus 8 Professional(或更新版本)
- 作用:用于绘制电路原理图并进行交互式仿真。
- 说明:务必安装 Professional 版本,它包含了丰富的微控制器模型和高级仿真功能。安装后需要加载 STM32 系列芯片的仿真模型库(通常安装包已包含)。
Keil uVision 5 MDK-ARM 或 STM32CubeIDE
- 作用:用于编写、编译和调试 STM32 的 C 语言程序。
- Keil:需要安装对应的 STM32F1 系列设备支持包(Device Family Pack)。
- STM32CubeIDE:ST 官方免费集成开发环境,集成了 CubeMX 配置工具,更适合新手。
STM32 程序烧录/调试驱动
- 作用:在 Proteus 中,我们需要将编译好的程序文件(.hex 或 .elf)加载到虚拟 MCU 中。
- 说明:使用 Keil 编译会生成
.hex文件;使用 CubeIDE 编译可配置生成.hex或.elf文件。Proteus 支持直接加载这些文件。
串口调试助手(如 SSCOM、XCOM)
- 作用:用于模拟手机 APP,通过虚拟串口与仿真电路中的蓝牙模块进行通信,发送控制指令。
系统环境
- 操作系统:Windows 7/10/11(Proteus 主要支持 Windows 平台)。
- 磁盘空间:预留至少 2GB 空间用于安装软件和存储项目文件。
- 权限:确保有权限安装软件并在 C 盘创建文件。
4. 电路设计与 Proteus 工程搭建
这是项目的硬件基础。我们将在 Proteus 中绘制出整个系统的原理图。
4.1 新建 Proteus 工程
- 打开 Proteus ISIS。
- 点击
File->New Project。设置工程名称(如Bladeless_Fan_Lamp)和存储路径。 - 在创建原理图步骤中,选择
DEFAULT模板即可。 - 在 PCB 设计步骤选择
No(我们只做仿真)。 - 在固件步骤选择
No(我们将在 Keil/CubeIDE 中单独编写程序)。
4.2 放置并连接核心元器件
点击左侧工具栏的P按钮,从元件库中搜索并放置以下关键元件:
- 微控制器:
STM32F103C8 - 晶体振荡器:
CRYSTAL(8MHz),用于提供系统主时钟。 - 负载电容:
CAP(22pF),连接在晶振两端。 - 复位电路:
RES(10kΩ 上拉电阻) 和BUTTON(复位按键)。 - 电源:
POWER和GROUND。 - 风扇驱动:用一个
DC MOTOR(直流电机)模型来模拟无叶风扇的电机,通过一个NPN三极管(如 2N2222)或MOSFET驱动,由 MCU 的 PWM 引脚控制。 - 台灯驱动:用多个
LED(发光二极管)并联来模拟灯条,同样通过一个三极管驱动,由另一个 PWM 引脚控制。可以添加一个RES(如 220Ω)作为限流电阻。 - 显示模块:
OLED(128x64),选择 I2C 接口的模型(如OLED12864-I2C)。 - 蓝牙模块:常用模型是
HC-05或HC-06。在 Proteus 中可能直接搜索BLUETOOTH或使用COMPIM(物理接口映射)组件配合虚拟串口来模拟。 - 控制按键:放置几个
BUTTON用于本地手动控制风扇和灯的档位。
4.3 关键电路连接示意图
以下是核心连接的逻辑说明(具体引脚号需在程序中定义一致):
- STM32 最小系统:连接 8M 晶振、复位电路、电源和地。
- 风扇驱动电路:STM32 的一个 PWM 引脚(如 PA0) -> 三极管基极电阻 -> NPN 三极管基极。三极管集电极接
DC MOTOR和电源,发射极接地。 - 台灯驱动电路:STM32 的另一个 PWM 引脚(如 PA1) -> 三极管基极电阻 -> 另一个 NPN 三极管基极。三极管集电极接
LED阳极和电源,LED 阴极通过限流电阻接地。 - OLED 显示:STM32 的 I2C1 引脚(PB6: SCL, PB7: SDA) 连接至 OLED 模型的对应引脚。
- 蓝牙模块:STM32 的一个 USART 引脚(如 PA9: TX, PA10: RX) 连接至
HC-05模型的RXD和TXD。HC-05的VCC和GND接电源和地。 - 本地按键:连接几个 GPIO 到
BUTTON,另一端接地,配置为上拉输入模式,按键按下时为低电平。
绘制完成后,你的原理图应包含一个能独立运行的虚拟硬件系统。务必使用Net Label功能标注关键网络,使图纸清晰易读。
5. 固件开发:STM32 程序编写
接下来,我们为 STM32F103C8 编写控制程序。这里以 Keil uVision 5 为例,简述关键步骤。
5.1 创建 Keil 工程并配置外设
- 打开 Keil,创建新工程,选择设备
STM32F103C8。 - 在
Manage Run-Time Environment窗口中,添加必要的软件组件,如Device中的Startup,以及CMSIS和Device下的标准外设库。 - 使用
STM32CubeMX工具进行图形化引脚配置是一个更高效的方法(即使最终用 Keil 编译)。在 CubeMX 中:- 选择 MCU 型号
STM32F103C8Tx。 - 配置时钟树,选择外部高速晶振(HSE),将系统时钟设置为 72MHz。
- 引脚配置:
- 将 PA0、PA1 配置为
TIM2_CH1和TIM2_CH2,生成 PWM。 - 将 PB6、PB7 配置为
I2C1_SCL和I2C1_SDA。 - 将 PA9、PA10 配置为
USART1_TX和USART1_RX,模式为异步通信。 - 将连接按键的引脚(如 PA2、PA3)配置为
GPIO_Input,上拉模式。
- 将 PA0、PA1 配置为
- 生成代码,选择 MDK-ARM V5 作为工具链。
- 选择 MCU 型号
5.2 核心功能代码实现
将生成的代码导入 Keil 工程。在主循环 (main.c的while(1))中,你需要实现以下逻辑:
// 伪代码逻辑示例 #include "main.h" #include "stdio.h" // 用于 sprintf #include "string.h" // 用于 strcmp #include "oled.h" // 假设有 OLED 驱动库 #include "motor.h" // 风扇控制函数 #include "light.h" // 灯光控制函数 UART_HandleTypeDef huart1; TIM_HandleTypeDef htim2; I2C_HandleTypeDef hi2c1; uint8_t uart_rx_buffer[10]; uint8_t uart_rx_len = 0; char cmd_buffer[20]; // 风扇和灯的状态变量 uint8_t fan_speed = 0; // 0:关, 1:低速, 2:中速, 3:高速 uint8_t light_level = 0; // 0:关, 1:低亮, 2:中亮, 3:高亮 int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); MX_I2C1_Init(); MX_TIM2_Init(); HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // 启动风扇 PWM 通道 HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2); // 启动灯光 PWM 通道 OLED_Init(); // 初始化 OLED OLED_Clear(); OLED_ShowString(0, 0, "Fan:OFF Light:OFF"); // 开启串口接收中断 HAL_UART_Receive_IT(&huart1, uart_rx_buffer, 1); while (1) { // 1. 扫描本地按键 if(HAL_GPIO_ReadPin(KEY_FAN_GPIO_Port, KEY_FAN_Pin) == GPIO_PIN_RESET){ HAL_Delay(50); // 消抖 fan_speed = (fan_speed + 1) % 4; // 循环切换 0~3 set_fan_speed(fan_speed); // 设置 PWM 占空比 update_display(); } // 类似处理灯光按键... // 2. 主循环中可添加其他任务 HAL_Delay(10); } } // 串口接收中断回调函数 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART1){ if(uart_rx_buffer[0] == '\n'){ // 假设指令以换行符结束 cmd_buffer[uart_rx_len] = '\0'; // 字符串终结 process_bluetooth_cmd(cmd_buffer); // 处理蓝牙指令 uart_rx_len = 0; } else { cmd_buffer[uart_rx_len++] = uart_rx_buffer[0]; } // 重新开启接收中断 HAL_UART_Receive_IT(&huart1, uart_rx_buffer, 1); } } // 处理蓝牙指令函数 void process_bluetooth_cmd(char* cmd){ if(strcmp(cmd, "FAN_ON_1") == 0){ fan_speed = 1; } else if(strcmp(cmd, "FAN_ON_2") == 0){ fan_speed = 2; } else if(strcmp(cmd, "FAN_OFF") == 0){ fan_speed = 0; } else if(strcmp(cmd, "LIGHT_ON_1") == 0){ light_level = 1; } else if(strcmp(cmd, "LIGHT_ON_3") == 0){ light_level = 3; } else if(strcmp(cmd, "LIGHT_OFF") == 0){ light_level = 0; } // ... 其他指令 set_fan_speed(fan_speed); set_light_level(light_level); update_display(); } // 更新 OLED 显示函数 void update_display(){ char str[32]; OLED_Clear(); sprintf(str, "Fan:%s", (fan_speed==0)?"OFF":(fan_speed==1)?"LOW":(fan_speed==2)?"MID":"HIGH"); OLED_ShowString(0, 0, str); sprintf(str, "Light:%s", (light_level==0)?"OFF":(light_level==1)?"LOW":(light_level==2)?"MID":"HIGH"); OLED_ShowString(0, 2, str); // 第二行显示 }5.3 编译与生成烧录文件
- 在 Keil 中配置好编译选项,确保没有错误。
- 点击
Build(F7) 编译工程。 - 编译成功后,在工程目录的
Objects文件夹下会生成.hex文件。记下这个文件的路径。
6. 仿真联调与功能测试
这是最关键的步骤,我们将把程序“烧录”到 Proteus 的虚拟芯片中,并观察整个系统是否按预期工作。
6.1 加载程序文件到 Proteus
- 回到 Proteus,双击原理图中的
STM32F103C8芯片,打开属性编辑框。 - 在
Program File一栏,点击文件夹图标,浏览并选择你在 Keil 中生成的.hex文件。 - 在
Clock Frequency一栏,输入72MHz(与程序中的系统时钟配置一致)。 - 点击
OK保存。
6.2 启动仿真与基础测试
- 点击 Proteus 界面左下角的
Play(运行) 按钮,启动仿真。 - 观察最小系统:查看晶振引脚是否有波形,复位电路电压是否正常。
- 测试本地按键:用鼠标点击原理图中的按键,模拟按下动作。此时,你应该能看到:
- 对应的 LED(风扇/灯光指示)亮度发生变化(PWM 效果在仿真中可能表现为亮度渐变或闪烁频率变化,电机模型可能表现为转速变化)。
- OLED 屏幕上显示的文字内容随之更新,例如从 “Fan:OFF” 变为 “Fan:LOW”。
- 测试 PWM 输出:可以右键点击控制风扇或灯光的 PWM 引脚,选择
Place Voltage Probe放置一个电压探针。再次运行仿真,观察探针处的电压波形,应能看到脉宽变化的方波。
6.3 蓝牙通信功能测试
测试蓝牙功能需要虚拟串口工具。我们使用Virtual Serial Port Driver(如 VSPD) 创建一对虚拟 COM 口,让 Proteus 和串口调试助手通过它们通信。
- 创建虚拟串口对:打开 VSPD,添加一对新的虚拟串口,例如
COM3和COM4。 - 配置 Proteus 中的蓝牙/串口模型:
- 如果你使用的是
COMPIM组件,双击它,在属性中设置Physical Port为COM3(假设),波特率设置为9600(需与程序中的huart1.Init.BaudRate一致)。 - 如果你使用的是
HC-05模型,它可能内置了串口连接属性,同样需要设置对应的 COM 口和波特率。
- 如果你使用的是
- 配置串口调试助手:
- 打开 SSCOM 等工具,选择串口号为
COM4(与 Proteus 配对的另一个口)。 - 设置相同的波特率(9600)、数据位(8)、停止位(1)、无校验位。
- 打开 SSCOM 等工具,选择串口号为
- 进行通信测试:
- 在 Proteus 中启动仿真。
- 在串口调试助手的发送区输入你在程序中定义的指令,例如
FAN_ON_1,然后点击发送。注意指令格式,如是否需要加回车换行符,需与程序中的判断逻辑 (\n) 匹配。 - 观察仿真系统:风扇指示 LED 应变亮(或电机开始转动),OLED 显示应更新为 “Fan:LOW”。
- 尝试发送
LIGHT_OFF、FAN_ON_3等指令,验证所有控制功能。
7. 资源占用与性能观察(仿真视角)
在仿真环境中,“性能”主要指程序逻辑的正确性和系统资源的虚拟占用情况。
- CPU 负载观察:Proteus 在仿真时,电脑的实际 CPU 使用率会升高。你可以通过 Windows 任务管理器观察
Proteus.exe进程的 CPU 占用。一个简单的 STM32 程序通常不会造成过高负载。如果仿真运行极其缓慢,可能是电路存在逻辑错误导致仿真计算复杂(如短路、振荡电路未起振)。 - 内存与 Flash 占用:在 Keil 的编译输出窗口,可以看到程序代码大小 (
Code) 和内存占用 (RO-data,RW-data,ZI-data)。对于 STM32F103C8(64KB Flash, 20KB RAM),本项目代码通常只占用一小部分,远未达到芯片极限。 - 外设冲突排查:在仿真中,如果某个功能不正常(如 OLED 不显示、PWM 无输出),应重点检查:
- 引脚复用冲突:在 CubeMX 中检查是否有同一个引脚被重复配置为不同功能。
- 外设时钟未使能:在代码中确认相关外设的时钟(
__HAL_RCC_xxx_CLK_ENABLE())已开启。 - 初始化顺序:确保外设初始化(如
HAL_TIM_PWM_Init)在启动 PWM(HAL_TIM_PWM_Start)之前完成。
- 仿真速度:点击 Proteus 左下角的仿真控制按钮,可以调整仿真速度。对于包含数字和模拟混合的电路,全速运行可能较慢,可以适当提高仿真速度或只运行关键部分进行调试。
8. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| Proteus 仿真无法启动或立即停止 | 1. MCU 程序文件 (.hex) 路径错误或损坏。 2. 晶振电路连接错误或频率设置不对。 3. 电源网络未全局连接。 | 1. 双击 MCU,确认 Program File 路径正确。 2. 检查晶振两端是否接有负载电容,MCU 属性中 Clock Frequency 是否设置。 3. 检查原理图中是否所有 VCC和GND都正确连接。 | 1. 重新编译生成 .hex 文件并加载。 2. 修正晶振电路,确保频率与代码一致。 3. 使用电源端口 ( POWER) 和地端口 (GROUND) 连接所有需要供电的元件。 |
| 按键控制无反应 | 1. GPIO 引脚配置模式错误(应为输入上拉)。 2. 按键电路连接错误(应一端接 GPIO,一端接地)。 3. 程序消抖处理不当或扫描逻辑有误。 | 1. 在 CubeMX 或代码中检查按键引脚配置。 2. 用电压探针测量按键按下/释放时引脚电平变化。 3. 在 Keil 中使用软件仿真或 Proteus 中单步调试,观察按键处理函数是否被触发。 | 1. 重新配置 GPIO 为GPIO_MODE_INPUT和GPIO_PULLUP。2. 修正电路连接。 3. 优化按键扫描代码,确保能稳定检测到边沿变化。 |
| OLED 屏幕无显示 | 1. I2C 引脚 (SCL/SDA) 接反或未连接。 2. I2C 初始化失败(时钟未使能、速率过高)。 3. OLED 驱动代码有误或初始化序列不对。 | 1. 检查原理图连接。 2. 在代码中检查 HAL_I2C_Init的返回值,或添加HAL_I2C_MspInit中的时钟使能代码。3. 使用逻辑分析仪(Proteus 中有)抓取 I2C 总线波形,看是否有起始信号、地址和数据传输。 | 1. 交换 SCL 和 SDA 线连接。 2. 降低 I2C 时钟速度(如 100kHz),确保上拉电阻已连接(仿真中有时需在引脚加外部上拉电阻模型)。 3. 核对 OLED 型号的驱动芯片(如 SSD1306)数据手册,修正初始化命令。 |
| 蓝牙指令无法控制 | 1. 虚拟串口未正确配对或占用。 2. 串口波特率、数据格式不匹配。 3. 程序串口接收中断未正确开启或数据处理逻辑错误。 4. 指令格式(如结尾符)不匹配。 | 1. 确认 VSPD 创建的 COM 口对,且未被其他程序占用。 2. 对比 Proteus 中 COMPIM/HC-05 属性与代码中 huart1.Init的配置。3. 在串口中断回调函数 HAL_UART_RxCpltCallback中设置断点或发送调试信息。4. 用串口调试助手以十六进制发送,查看实际接收到的字节。 | 1. 关闭可能占用 COM 口的程序,重新创建虚拟串口对。 2. 统一配置波特率为 9600, 8N1。 3. 确保在主函数中调用了 HAL_UART_Receive_IT启动接收中断。4. 调整程序中的指令解析逻辑,使其与调试助手发送的格式完全一致。 |
| PWM 输出无变化 | 1. 定时器时钟未使能或配置错误。 2. PWM 输出通道未正确配置或未启动。 3. 控制 PWM 占空比的函数未生效。 | 1. 检查 CubeMX 中定时器(如 TIM2)的配置,预分频器、自动重载值是否设置。 2. 确认代码中调用了 HAL_TIM_PWM_Start。3. 单步调试,查看修改 CCR寄存器(或调用__HAL_TIM_SET_COMPARE)的函数是否被执行。 | 1. 在 CubeMX 中重新配置定时器,生成代码。 2. 在 main函数中,确保在while(1)之前启动了 PWM。3. 使用电压探针观察 PWM 引脚波形,验证占空比是否随控制变量改变。 |
9. 项目扩展与最佳实践
完成基础功能后,你可以尝试以下扩展,让项目更完善:
- 增加温度传感器:添加一个 DS18B20 或 DHT11 模型,在 OLED 上显示环境温度/湿度,并实现温度过高自动开启风扇的简单智能逻辑。
- 实现定时功能:利用 STM32 的 RTC 或 SysTick 定时器,为风扇和灯光增加定时关闭功能,并通过蓝牙设置定时时间。
- 优化功耗仿真:虽然 Proteus 对功耗仿真有限,但你可以通过代码控制不用的外设时钟关闭,进入睡眠模式等,学习低功耗编程思想。
- 设计更友好的 UI:优化 OLED 显示内容,增加图标、进度条来直观显示风速和亮度。
- 移植到真实硬件:这是最终目标。根据仿真成功的电路图,采购 STM32 最小系统板、OLED 屏、HC-05 模块、电机驱动模块(如 L298N)、LED 灯珠和 MOS 管等,将程序下载到实物进行调试。实物调试需额外考虑电源设计、信号干扰、PCB 布局等问题。
最佳实践建议:
- 版本管理:使用 Git 对代码和 Proteus 工程文件进行版本控制,每次重大修改前提交一次。
- 模块化编程:将风扇控制、灯光控制、OLED 驱动、蓝牙处理分别写成独立的
.c/.h文件,提高代码可读性和可移植性。 - 仿真与实物并进:在仿真中主要验证逻辑和算法,复杂的驱动(如无刷电机)和通信协议(如蓝牙配对)仍需以实物测试为准。
- 文档记录:保存好电路图、程序流程图、引脚分配表和遇到的问题及解决方案,形成完整的项目文档。
通过这个“无叶风扇台灯仿真”项目,你不仅掌握了 Proteus 和 STM32 联合开发的基本流程,更实践了嵌入式系统从需求分析、硬件设计、软件编程到调试测试的全过程。这套方法可以迁移到绝大多数基于 MCU 的电子产品开发中,是嵌入式工程师的核心技能。建议你以此为蓝本,尝试改造和创造属于自己的智能设备仿真项目。