ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于STM32的实验室消防预警控制器设计与实现:从硬件选型到Proteus仿真验证

基于STM32的实验室消防预警控制器设计与实现:从硬件选型到Proteus仿真验证 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-05VDCSTM32的GPIO经过ULN2003达林顿管驱动避免电流倒灌烧芯片。人机交互模块是一块0.96寸OLEDI2C接口显示当前温度、烟雾浓度、系统状态。四个按键用来设置阈值和手动消音。通信模块用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和PD1OSC_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.3V12位分辨率下1LSB等于0.8mV这个精度足够了。NTC热敏电阻的分压电路需要仔细算一下。我用的是10K25℃的NTCB值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到30mAS8050的集电极电流最大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数据具体数据N1校验和从字节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接口的SSD1306Proteus里有这个模型但显示效果和实际的有差异仿真的时候主要看数据对不对不用太在意显示效果。仿真的时候有个坑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的RXRX接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。仿真的时候如果代码里配置了PLLProteus可能不会正确模拟导致定时器时间不对。我的做法是在代码里加一个宏定义仿真的时候用内部时钟实际硬件用外部晶振。这样仿真和实物的定时器行为一致。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 DebuggerSettings里选择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万次如果频繁动作建议用固态继电器替代。这个项目我从画原理图到调试完成用了大约两周时间其中仿真调试占了一半。仿真虽然不能完全替代实物调试但可以提前发现很多逻辑错误节省不少时间。开源出来是希望更多人能参考这个方案少走一些弯路。如果你在复现过程中遇到问题可以先检查电源和时钟这两个是大多数问题的根源。
返回列表