☰
基于STM32的实验室消防预警控制器设计与实现:从硬件选型到Proteus仿真验证
2026/9/27 10:21:44 网站建设 项目流程

1. 项目缘起与整体设计思路

1.1 为什么选择STM32做消防预警控制器

实验室场景的消防预警和商用楼宇那种动辄几百个探测点的大系统完全是两码事。一个标准的高校实验室或者企业研发实验室,面积通常在40到120平米之间,需要监控的点位无非是温度、烟雾浓度、可燃气体这几类,点位数量一般不超过16路。这种规模下,用PLC方案成本太高,用纯51单片机又显得捉襟见肘——ADC精度不够、中断响应慢、多路并行采集时容易丢数据。

STM32F103C8T6这颗芯片在这个场景里几乎是量身定做的。72MHz主频、12位ADC、多达10个定时器、丰富的中断源,关键是价格便宜,一片不到十块钱。我选的是LQFP48封装,焊起来不费劲,引脚也够用。整个系统需要的外设资源大致如下:ADC通道用来接MQ-2烟雾传感器和NTC热敏电阻,GPIO用来驱动声光报警和继电器输出,USART用来和上位机通信,I2C用来挂OLED显示屏做本地状态显示。

这里有个选型上的细节值得说:很多人做消防预警喜欢用DHT11这种数字温湿度传感器,接线简单,一根数据线搞定。但DHT11的响应速度太慢,采样周期最快也要1秒,而且精度只有±2℃。消防预警场景下,温度变化的检测延迟直接关系到报警及时性,所以我用的是NTC热敏电阻加分压电路,接到STM32的ADC引脚上,采样周期可以做到10ms一次,配合滑动平均滤波,实际响应时间在200ms以内。这个选择在后面仿真环节也会体现出优势,因为ADC采集在Proteus里比单总线协议好模拟得多。

1.2 系统架构的模块化拆分

整个系统我拆成了五个独立模块,每个模块单独调试通过后再联调。这种做法的好处是出了问题容易定位,不会出现“报警不响到底是传感器坏了还是主控没收到”这种扯皮情况。

传感采集模块负责把物理量转换成电信号再变成数字量。烟雾检测用MQ-2,它的输出是模拟电压,浓度越高电压越高,接到PA0引脚。温度检测用10K NTC热敏电阻和10K精密电阻组成分压网络,中点接到PA1。这两路ADC采集用DMA搬运,不占用CPU时间。

主控决策模块是STM32F103C8T6最小系统,负责轮询采集数据、执行阈值判断、管理报警状态机。这里我用了一个简单的状态机:正常态、预警态、报警态、故障态。状态之间的迁移条件在代码里写得很清楚,后面会详细说。

报警输出模块包括三部分:本地蜂鸣器(有源蜂鸣器,GPIO直接驱动三极管)、LED指示灯(红黄绿三色对应不同状态)、继电器输出(用来联动排风扇或切断非关键电源)。继电器我用的是5V驱动的SRD-05VDC,STM32的GPIO经过ULN2003达林顿管驱动,避免电流倒灌烧芯片。

人机交互模块是一块0.96寸OLED,I2C接口,显示当前温度、烟雾浓度、系统状态。四个按键用来设置阈值和手动消音。

通信模块用USART1,通过CH340G转USB和上位机通信,上报数据和接收远程指令。这里我用了STM32的USB虚拟串口功能,但考虑到仿真环境对USB支持不好,实际代码里保留了USART和USB两种方式,通过宏定义切换。

1.3 开源内容的组织方式

这个项目开源的东西分三块:代码、原理图、仿真工程。代码用Keil MDK5工程组织,目录结构是标准的CMSIS加StdPeriphLib加User三层。原理图我用的是嘉立创EDA画的,导出PDF和Gerber两种格式。仿真用的是Proteus 8.13,包含原理图文件和编译好的hex文件。

开源的时候有个坑要提醒:Keil工程里的绝对路径一定要改成相对路径,不然别人下载下来编译全是红叉。具体做法是在Options for Target的Output和Listing选项卡里,把路径都改成.\Objects\和.\Listings\这种相对形式。另外StdPeriphLib的版本要注明,我用的是V3.5.0,不同版本之间有些宏定义不一样,别人用V3.6.0编译可能会报错。

2. 硬件设计核心细节与原理图解析

2.1 STM32最小系统的关键外围电路

最小系统看着简单,但有几个地方如果画错了,板子回来就是一堆问题。我第一版就踩过坑,这里把关键点都列出来。

电源部分:STM32F103的VDD范围是2.0V到3.6V,典型3.3V。我用的是AMS1117-3.3,输入5V,输出3.3V。注意AMS1117的压差在1.1V左右,5V输入没问题,但如果你用3.7V锂电池供电,输出可能只有2.6V,芯片工作不稳定。输入输出各加一个10uF钽电容和一个100nF陶瓷电容,钽电容负责低频滤波,陶瓷电容负责高频去耦。每个VDD引脚旁边都要放一个100nF,这个不能省,我见过有人只放一个总电容,结果ADC采集出来的数据跳得厉害。

复位电路:NRST引脚接10K上拉电阻到3.3V,再并一个100nF电容到地。按键复位是可选 的,我保留了复位按键,方便调试。注意NRST内部有弱上拉,但外部还是要加,不然复位引脚容易受干扰。

晶振电路:8MHz主晶振接PD0和PD1(OSC_IN和OSC_OUT),两个22pF负载电容。32.768kHz的RTC晶振我沒用,因为这个项目不需要精确计时。晶振布局要尽量靠近芯片,走线要短且等长,底下不要走其他信号线。我第一版把晶振放在板子边缘,结果起振不稳定,后来挪到芯片旁边就好了。

启动模式:BOOT0接10K下拉到地,BOOT1接10K下拉到地,这样默认从Flash启动。如果你要串口下载,BOOT0要跳线到3.3V。我在板子上留了一个跳线帽,方便切换。

调试接口:SWD接口只需要SWDIO和SWCLK两根线,加上3.3V和GND,四根线就够了。我用的排针是2.54mm间距的4P排针,标注清楚引脚顺序,不然接反了可能烧调试器。

2.2 传感器信号调理电路的设计考量

MQ-2烟雾传感器的输出特性是非线性的,而且受环境温湿度影响很大。它的加热丝需要5V供电,加热电流大约150mA,这个电流不算小,所以电源走线要够宽。输出端接一个10K可调电阻做负载,调节输出电压范围。我实测下来,洁净空气中输出电压大约0.3V到0.5V,有烟雾时能到2V以上。STM32的ADC参考电压是3.3V,12位分辨率下,1LSB等于0.8mV,这个精度足够了。

NTC热敏电阻的分压电路需要仔细算一下。我用的是10K@25℃的NTC,B值3950。分压电阻选10K,接3.3V。25℃时NTC阻值10K,分压中点电压是1.65V。温度升高时NTC阻值下降,中点电压升高。这个电路的输出电压范围大概在0.5V到3.0V之间,对应温度范围-10℃到80℃,覆盖了实验室场景的需求。

但NTC的阻值-温度关系是指数型的,直接读ADC值再线性映射到温度会有很大误差。我的做法是在代码里用查表法加线性插值。具体来说,把-10℃到80℃分成90个点,每个点对应一个ADC值,存成一个const数组。实际采集到ADC值后,在表里找到相邻两个点,做线性插值。这样计算量小,精度也能做到±0.5℃以内。这个表可以用Excel算,公式是:ADC = 4096 * Rntc / (Rntc + 10000),其中Rntc = 10000 * exp(B * (1/T - 1/298.15)),T是开尔文温度。

2.3 报警输出与继电器驱动电路

蜂鸣器驱动我用的是S8050三极管,基极串一个1K电阻接STM32的GPIO,集电极接蜂鸣器负极,蜂鸣器正极接5V。这里要注意,有源蜂鸣器内部有振荡电路,通电就响,驱动电流大约20mA到30mA,S8050的集电极电流最大500mA,完全够用。如果是无源蜂鸣器,需要用PWM驱动,那就得用定时器输出PWM波,代码会复杂一些。我选的是有源蜂鸣器,简单可靠。

继电器驱动用ULN2003,这是一个七路达林顿管阵列,每路最大500mA,输入兼容3.3V逻辑。STM32的GPIO接到ULN2003的输入端,输出端接继电器线圈。继电器线圈两端要并一个续流二极管,我用的是1N4148,方向是阴极接5V,阳极接ULN2003输出端。这个二极管的作用是继电器断电时线圈产生的反向电动势通过二极管泄放,保护ULN2003不被击穿。我第一版忘了加这个二极管,结果用了不到一个月ULN2003就烧了。

LED指示灯我用的是共阳极接法,三个LED的阳极接3.3V,阴极各串一个1K电阻接GPIO。STM32的GPIO灌电流能力比拉电流强,所以低电平点亮更合适。红色LED对应报警态,黄色对应预警态,绿色对应正常态。三个LED的限流电阻都是1K,电流大约3mA,亮度足够又不费电。

2.4 原理图绘制中的实操要点

用嘉立创EDA画原理图有几个地方要注意。首先是栅格设置,我一般用10mil栅格画线,5mil栅格放元件。栅格太大会导致引脚对不齐,太小又不好操作。在“设置”菜单里可以改栅格大小,快捷键是G。

其次是网络标签的使用。STM32的引脚很多,如果全部用线连,原理图会像蜘蛛网一样。我的做法是给每个功能模块加网络标签,比如ADC_IN0、BUZZER、LED_RED这些。网络标签要放在引脚延长线上,不要直接放在引脚上,不然导出网表的时候可能识别不到。

第三是元件符号的选择。嘉立创EDA自带的库里有STM32F103C8T6的符号,但引脚排列和实际芯片不一样。我建议自己画一个符号,按照实际引脚的物理位置排列,这样画PCB的时候不容易出错。画符号的时候,电源引脚放在顶部,地引脚放在底部,IO引脚按端口分组放在两侧。

导出PDF的时候,选择“文件”->“导出”->“PDF”,分辨率选300dpi,颜色选彩色。这样导出的PDF放大后依然清晰,方便别人查看。如果要导出Gerber文件打板,在“制造”->“PCB制板文件”里生成,记得勾选“包括钻孔文件”。

3. 软件架构与核心代码实现

3.1 主程序状态机与任务调度

主程序我用了一个简单的时间片轮询架构,没有上RTOS。对于这个项目来说,RTOS有点杀鸡用牛刀,而且会增加代码复杂度和调试难度。时间片轮询的核心是一个1ms的SysTick中断,在中断里给各个任务计数器减一,主循环里判断计数器是否到零,到了就执行对应任务。

// 任务调度结构体 typedef struct { void (*task)(void); uint16_t period; uint16_t counter; } TaskType; TaskType tasks[] = { {Task_ADC_Sample, 10, 0}, // 10ms采集一次 {Task_Data_Process, 50, 0}, // 50ms处理一次 {Task_Alarm_Check, 100, 0}, // 100ms检查一次 {Task_Display_Update, 200, 0}, // 200ms刷新显示 {Task_Comm_Report, 500, 0}, // 500ms上报一次 };

SysTick中断服务函数里遍历任务数组,每个counter减一,减到零就置位一个标志。主循环里检查标志,执行对应任务,然后把counter重置为period。这种方式的优点是任务执行时间可控,不会因为某个任务卡死导致整个系统无响应。缺点是如果某个任务执行时间超过它的周期,会导致任务堆积。所以每个任务的实际执行时间要远小于它的周期,我实测下来最长的任务(Display_Update)执行时间大约15ms,周期200ms,余量充足。

ADC采集任务里,我用了DMA加定时器触发的方案。TIM2设置为1kHz的更新频率,每次更新事件触发一次ADC转换,ADC转换完成后DMA自动把数据搬到内存数组。这样CPU完全不参与采集过程,只需要在需要的时候去读数组就行。DMA缓冲区大小设为16,采集16次后触发DMA传输完成中断,在中断里把数据搬到一个处理缓冲区,然后重新配置DMA。这种双缓冲机制可以保证数据不会丢失。

3.2 温度与烟雾浓度的数据处理算法

ADC采集到的原始数据是0到4095的整数,需要转换成实际的物理量。温度转换用查表加插值,前面已经说了。烟雾浓度转换稍微复杂一点,因为MQ-2的输出和烟雾浓度不是线性关系,而且受温度和湿度影响。

我的做法是先用一个简单的公式把ADC值转换成电压:V = ADC * 3.3 / 4096。然后根据MQ-2的数据手册,在洁净空气中输出电压约为0.4V,对应浓度200ppm(这个值是近似值,实际需要标定)。浓度和电压的关系近似为:ppm = 200 * (V / 0.4)^(1/0.6)。这个公式是我根据数据手册的曲线拟合出来的,实际使用的时候需要根据具体传感器标定。

为了消除温湿度影响,我在代码里加了一个补偿系数。温度每升高1℃,MQ-2的输出电压大约升高0.5%;湿度每升高1%RH,输出电压大约降低0.3%。补偿公式是:V_comp = V / (1 + 0.005 * (T - 25) - 0.003 * (H - 50))。这里的T和H是当前温度和湿度,25℃和50%RH是标定时的环境条件。这个补偿系数是经验值,不同批次的传感器可能有差异,需要根据实际情况调整。

数据滤波我用的是滑动平均加中值滤波的组合。滑动平均窗口大小是8,中值滤波窗口大小是5。具体做法是:先把最近8次采集的数据做滑动平均,然后对最近5次滑动平均的结果做中值滤波。这样既能消除随机噪声,又能抑制脉冲干扰。实测下来,经过滤波后的数据波动在±1%以内,满足消防预警的精度要求。

3.3 报警状态机的设计与实现

报警状态机是整个系统的核心逻辑,我用了一个枚举类型来定义状态:

typedef enum { STATE_NORMAL, // 正常 STATE_WARNING, // 预警 STATE_ALARM, // 报警 STATE_FAULT // 故障 } SystemState;

状态迁移条件如下表所示:

当前状态迁移条件目标状态动作
正常温度>预警阈值 或 烟雾>预警阈值预警黄灯亮,蜂鸣器间歇响
预警温度>报警阈值 或 烟雾>报警阈值报警红灯亮,蜂鸣器长响,继电器动作
预警温度<预警阈值-回差 且 烟雾<预警阈值-回差正常绿灯亮,蜂鸣器停
报警温度<报警阈值-回差 且 烟雾<报警阈值-回差预警黄灯亮,蜂鸣器间歇响
任意传感器故障(ADC值超范围)故障红灯闪烁,蜂鸣器间歇响

这里有个关键设计:回差。如果没有回差,当测量值在阈值附近波动时,状态会频繁切换,蜂鸣器会断断续续响,很烦人。我设的回差是预警阈值减2℃,报警阈值减5℃。烟雾浓度的回差是预警阈值减50ppm,报警阈值减100ppm。这个回差值可以根据实际场景调整,实验室环境比较稳定,回差可以小一点;如果环境波动大,回差就要大一些。

状态迁移的时候还要考虑延时确认。比如温度瞬间超过阈值,可能是干扰导致的,不应该立即报警。我的做法是连续3次检测都超过阈值才确认报警,每次检测间隔100ms,也就是300ms的确认时间。这个时间不能太长,否则报警不及时;也不能太短,否则容易误报。300ms是我实测下来比较合适的值。

3.4 通信协议与上位机交互

串口通信我用的是自定义的简单协议,帧格式如下:

字节内容说明
00xAA帧头
10x55帧头
2长度数据域长度
3命令0x01上报数据,0x02设置阈值,0x03消音
4~N数据具体数据
N+1校验和从字节2到字节N的累加和低8位

上报数据的时候,数据域包含温度(2字节,单位0.1℃)、烟雾浓度(2字节,单位ppm)、系统状态(1字节)。温度用有符号16位整数表示,比如25.3℃存成253。烟雾浓度用无符号16位整数,比如350ppm存成350。系统状态就是前面枚举的值。

设置阈值的时候,数据域包含预警温度阈值(2字节)、报警温度阈值(2字节)、预警烟雾阈值(2字节)、报警烟雾阈值(2字节)。上位机发过来后,STM32把这些值写到Flash的指定地址,掉电不丢失。Flash读写要注意,STM32F103的Flash页大小是1KB,写之前要先擦除整页。我用的地址是0x0800F000,这是最后一页,不会和代码冲突。

消音命令就是让蜂鸣器停止响,但状态机继续运行。如果状态从报警降到预警,蜂鸣器会重新开始间歇响。这个逻辑在代码里用一个标志位控制,消音的时候置位,状态迁移的时候清零。

4. 仿真验证与调试实录

4.1 Proteus仿真工程的搭建

Proteus仿真最大的好处是可以在没有硬件的情况下验证代码逻辑。我用的Proteus 8.13,元件库里有STM32F103C8T6的模型,但要注意,Proteus的STM32模型只支持部分外设,ADC和USART是支持的,USB不支持。所以仿真的时候我用的是USART通信,不是USB虚拟串口。

搭建仿真工程的步骤:先从元件库里面找到STM32F103C8T6、MQ-2、NTC热敏电阻、OLED、蜂鸣器、LED、按键这些元件,放到原理图里。然后双击STM32,在Program File里选择编译好的hex文件,在Crystal Frequency里填8MHz。注意Proteus的STM32模型默认时钟是8MHz,如果你在代码里用了PLL倍频到72MHz,需要在Proteus里也设置相应的时钟频率,否则仿真速度会不对。

MQ-2在Proteus里没有现成的模型,我用一个电位器代替。电位器的滑动端接STM32的ADC引脚,通过调节电位器来模拟烟雾浓度的变化。NTC热敏电阻Proteus里有,但它的阻值-温度曲线和实际的不完全一样,仿真的时候只能看个大概趋势。OLED我用的是I2C接口的SSD1306,Proteus里有这个模型,但显示效果和实际的有差异,仿真的时候主要看数据对不对,不用太在意显示效果。

仿真的时候有个坑:Proteus的ADC模型采样速度比实际慢很多,如果你在代码里用DMA加定时器触发,仿真可能会卡死。我的做法是在仿真的时候把ADC采样改成软件触发,不用DMA,直接读ADC数据寄存器。实际硬件上用DMA,仿真的时候用轮询,通过宏定义切换。这样仿真能跑起来,实际硬件性能也不受影响。

4.2 关键波形的观测与分析

仿真的时候可以用Proteus的虚拟示波器观察波形。我把示波器的通道A接到蜂鸣器驱动引脚,通道B接到LED驱动引脚,通道C接到ADC输入引脚。这样可以看到报警触发时各个信号的变化时序。

正常状态下,蜂鸣器引脚是低电平,LED绿灯引脚是低电平(低电平点亮),ADC输入是0.5V左右。当我把电位器调大,模拟烟雾浓度升高,ADC输入电压升高到2V以上,经过300ms确认时间后,蜂鸣器引脚开始输出方波(间歇响),LED红灯引脚变低电平。这个时序在示波器上看得清清楚楚。

这里有个细节:蜂鸣器间歇响的频率是1Hz,也就是响0.5秒停0.5秒。这个频率是在定时器中断里实现的,每500ms翻转一次蜂鸣器引脚。LED红灯是常亮,不闪烁。如果是故障状态,红灯闪烁频率是2Hz,和报警状态区分开。

ADC输入引脚的波形要注意,因为MQ-2的输出有噪声,实际波形不是一条直线,而是有毛刺的。我在代码里加了RC滤波,硬件上也在ADC输入引脚并了一个100nF电容到地。仿真的时候可以观察滤波前后的波形对比,滤波后明显平滑很多。

4.3 常见问题与排查技巧

问题一:仿真跑不起来,提示“Simulation Failed”

这个最常见的原因是hex文件路径不对,或者hex文件没有编译成功。检查Keil工程是否编译通过,在Objects目录下是否有hex文件。如果有hex文件但Proteus还是报错,检查Proteus的Program File路径是否指向了正确的hex文件。另外,Proteus的STM32模型对hex文件的格式有要求,必须是Intel HEX格式,Keil默认生成的就是这个格式。

问题二:ADC采集的数据一直是0或者一直是4095

先检查ADC引脚是否配置正确。STM32的ADC引脚是PA0到PA7,对应ADC通道0到7。如果你用的是PA0,通道就是0。在代码里要配置GPIO为模拟输入模式,不能配置成其他模式。然后检查ADC的初始化顺序:先使能ADC时钟,再配置ADC参数,最后使能ADC。如果顺序错了,ADC可能不工作。

问题三:串口通信收不到数据

先检查波特率是否一致。STM32的USART1挂载在APB2总线上,时钟是72MHz。波特率计算公式是:USARTDIV = 72000000 / (16 * 波特率)。比如9600波特率,USARTDIV = 468.75,整数部分是468,小数部分是0.75,对应BRR寄存器的值是0x1D4C。如果波特率算错了,收到的就是乱码。另外检查串口线是否接对,STM32的TX接CH340的RX,RX接CH340的TX,不要接反了。

问题四:继电器不动作

先检查ULN2003的输入引脚是否有高电平。用万用表测STM32的GPIO引脚,报警状态下应该是3.3V。如果没有,检查代码里是否配置了GPIO为推挽输出模式。如果有3.3V但继电器还是不动作,检查ULN2003的输出端是否有电压。ULN2003是达林顿管,输入端有电流时输出端对地导通,继电器线圈一端接5V,另一端接ULN2003输出端,导通时线圈通电。如果ULN2003输出端一直是高电平,可能是ULN2003坏了,换一片试试。

问题五:OLED不显示

先检查I2C地址是否正确。SSD1306的I2C地址通常是0x78或0x7A,取决于SA0引脚的电平。如果地址错了,OLED不会有任何反应。然后检查I2C的初始化顺序:先使能I2C时钟和GPIO时钟,再配置GPIO为复用开漏模式,最后配置I2C参数。如果用的是硬件I2C,还要注意STM32F103的I2C有已知的硬件bug,在某些情况下会卡死。我的做法是用软件I2C,用GPIO模拟I2C时序,虽然速度慢一点,但稳定可靠。

4.4 从仿真到实物的差异与注意事项

仿真通过不代表实物就能正常工作,这两者之间有几个关键差异要注意。

时钟差异:Proteus的STM32模型默认使用8MHz内部时钟,而实际硬件上我用的是8MHz外部晶振加PLL倍频到72MHz。仿真的时候如果代码里配置了PLL,Proteus可能不会正确模拟,导致定时器时间不对。我的做法是在代码里加一个宏定义,仿真的时候用内部时钟,实际硬件用外部晶振。这样仿真和实物的定时器行为一致。

ADC差异:Proteus的ADC模型是理想化的,没有噪声和偏移。实际硬件的ADC有偏移误差和增益误差,需要进行校准。STM32的ADC有自校准功能,在初始化的时候调用ADC_StartCalibration函数,等待校准完成后再使能ADC。校准可以消除偏移误差,但增益误差需要外部参考电压来校准。我的做法是用一个精密基准源(比如TL431)产生2.5V参考电压,接到ADC的一个通道上,采集这个通道的值来校准其他通道。

传感器差异:Proteus里的MQ-2模型是理想化的,输出和浓度是线性关系。实际的MQ-2输出是非线性的,而且需要预热。MQ-2的加热丝需要预热24小时以上才能稳定,刚上电的时候输出漂移很大。我的做法是在代码里加一个预热计时器,上电后前5分钟不报警,只显示“预热中”。5分钟后如果传感器还没稳定,继续等待,直到输出稳定为止。

电源差异:仿真的时候电源是理想的,没有纹波和噪声。实际硬件的电源纹波会影响ADC精度。我的做法是在AMS1117的输出端加一个LC滤波,L是一个10uH的电感,C是一个100uF的电解电容。这样可以把纹波降到10mV以内,ADC的跳动从±5LSB降到±1LSB。

5. 开源项目的组织与复现指南

5.1 代码工程的目录结构与编译配置

开源代码的目录结构我按照标准的三层架构组织:

FireAlarm/ ├── CMSIS/ # 内核支持文件 │ ├── core_cm3.h │ └── stm32f10x.h ├── StdPeriphLib/ # 标准外设库 │ ├── inc/ │ └── src/ ├── User/ # 用户代码 │ ├── main.c │ ├── stm32f10x_it.c │ ├── adc.c │ ├── usart.c │ ├── oled.c │ ├── relay.c │ └── state_machine.c ├── Objects/ # 编译输出 ├── Listings/ # 列表文件 └── Project.uvprojx # Keil工程文件

编译配置有几个关键点:在Options for Target的Target选项卡里,晶振频率填8MHz。在C/C++选项卡里,Define里加上USE_STDPERIPH_DRIVER和STM32F10X_MD。在Include Paths里加上CMSIS、StdPeriphLib/inc、User这三个路径。在Output选项卡里勾选Create HEX File。在Debug选项卡里选择ST-Link Debugger,Settings里选择SWD模式。

如果别人下载下来编译报错,最常见的原因是StdPeriphLib的版本不对。我在工程里用的是V3.5.0,如果别人用的是V3.6.0,有些宏定义会不一样。解决办法是在stm32f10x.h里检查版本号,或者直接把我用的StdPeriphLib一起打包进去。

5.2 原理图与PCB文件的导出规范

原理图我用嘉立创EDA画的,导出的时候要导出三种格式:PDF、PNG、源文件。PDF用于查看,PNG用于在博客里展示,源文件用于别人修改。导出PDF的时候,选择“文件”->“导出”->“PDF”,分辨率选300dpi,颜色选彩色,勾选“包括网络标签”和“包括元件参数”。

PCB文件我画的是双层板,尺寸是50mm x 40mm。导出Gerber的时候,选择“制造”->“PCB制板文件”,勾选所有层,包括顶层、底层、丝印层、阻焊层、助焊层、钻孔文件。导出的Gerber文件打包成zip,可以直接发给嘉立创打板。打板的时候要注意,板厚选1.6mm,铜厚选1oz,表面处理选HASL(有铅喷锡),这些是默认选项,不用改。

BOM表我用Excel整理,包含元件名称、型号、封装、数量、备注。关键元件的型号要写清楚,比如STM32F103C8T6、AMS1117-3.3、MQ-2、ULN2003、SRD-05VDC-SL-C。备注里写清楚替代型号,比如AMS1117-3.3可以用LM1117-3.3替代,ULN2003可以用ULN2003A替代。

5.3 仿真工程的分享与使用说明

Proteus仿真工程我打包成zip,包含Proteus工程文件(.pdsprj)和编译好的hex文件。别人下载后,用Proteus 8.13以上版本打开,直接点运行就可以看到仿真效果。如果Proteus版本低于8.13,可能会提示文件格式不兼容,需要升级Proteus。

仿真工程里我预设了几个场景:正常状态、预警状态、报警状态、故障状态。通过调节电位器可以切换场景。正常状态下,电位器在中间位置,ADC输入约1.5V。预警状态下,电位器调大,ADC输入约2.0V。报警状态下,电位器调到最大,ADC输入约2.5V。故障状态下,把电位器调到最小,ADC输入接近0V,触发传感器故障检测。

仿真的时候要注意,Proteus的STM32模型运行速度比实际慢很多,一个完整的报警流程可能需要几秒钟才能跑完。如果觉得太慢,可以在Proteus的“系统”->“设置动画选项”里把“每秒帧数”调低,这样仿真速度会快一些,但波形会不太流畅。

5.4 复现过程中的经验与避坑建议

焊接顺序:先焊电源部分,焊完测一下3.3V是否正常。然后焊STM32和晶振,焊完用ST-Link连一下,看能不能识别到芯片。然后焊传感器和驱动电路,最后焊OLED和按键。这样分步焊接,出了问题容易定位。

调试工具:ST-Link V2是必须的,用来下载程序和调试。串口助手用来查看上报数据,我用的SSCOM,功能简单够用。万用表用来测电压和通断,示波器如果有的话更好,没有的话用逻辑分析仪也行。

常见焊接问题:STM32的引脚间距是0.5mm,焊接的时候容易连锡。我的做法是用刀头烙铁,温度调到350℃,加少量焊锡,用拖焊的方式焊接。焊完后用放大镜检查,如果有连锡,用吸锡线吸掉。晶振的负载电容要尽量靠近晶振,走线要短,不然起振不稳定。

代码调试技巧:如果程序跑不起来,先在main函数开头加一个LED闪烁,确认程序是否在运行。如果LED不闪,检查启动文件和时钟配置。如果LED闪但功能不对,用串口打印调试信息,把关键变量的值打印出来。我习惯在状态机里加一个打印语句,每次状态迁移的时候打印当前状态和触发条件,这样调试起来很直观。

阈值设置建议:实验室环境的温度预警阈值建议设35℃,报警阈值设45℃。烟雾预警阈值建议设300ppm,报警阈值设500ppm。这些值可以根据实际情况调整,如果实验室有易燃易爆物品,阈值要设低一些。设置阈值的时候要留足够的余量,避免误报。比如夏天实验室温度可能到32℃,预警阈值设35℃就有点低,建议设38℃。

维护建议:MQ-2传感器需要定期标定,建议每半年标定一次。标定方法是在洁净空气中记录ADC值,然后在已知浓度的标准气体中记录ADC值,用这两点重新计算浓度公式。NTC热敏电阻的精度会随时间漂移,建议每年更换一次。继电器的触点寿命大约10万次,如果频繁动作,建议用固态继电器替代。

这个项目我从画原理图到调试完成用了大约两周时间,其中仿真调试占了一半。仿真虽然不能完全替代实物调试,但可以提前发现很多逻辑错误,节省不少时间。开源出来是希望更多人能参考这个方案,少走一些弯路。如果你在复现过程中遇到问题,可以先检查电源和时钟,这两个是大多数问题的根源。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询