基于STM32与GPRS模块的远程抄表终端硬件设计与软件实现
1. 项目概述与核心思路
最近在做一个远程抄表的项目,核心需求是通过GPRS网络,把分散在各地的电表、水表数据集中回传到服务器。市面上成熟的DTU模块当然有,但成本摆在那里,而且功能上未必完全贴合自己的特定需求,比如有些需要特定的协议解析,有些对低功耗有极致要求。所以,琢磨着自己用STM32搭一个,既能深度定制,又能把成本压到最低,顺便也把STM32和GPRS模块玩得更透一些。
这个项目的硬件核心就两块:主控MCU和GPRS通信模块。主控我选了意法半导体的STM32F101C8T6,属于STM32F1系列的入门款,但该有的都有:72MHz的Cortex-M3内核,64KB Flash,20KB RAM,最关键的是它有两个USART(串口),这正好满足我的需求——一个用来和GPRS模块“对话”,另一个用来通过RS-485总线与现场的电表们“交谈”。GPRS模块方面,我用了经典的西门子TC35i。这模块虽然有些年头了,但胜在稳定、资料多、二手市场存货量大且价格非常便宜,对于这种数据量不大、实时性要求不高的抄表应用,完全够用。
画原理图的过程,其实就是一次资源权衡和接口规划的过程。除了确保核心的串口通信,还得考虑一些“后勤”问题:采集的数据存哪里?设备参数掉电了不能丢吧?万一要现场升级程序怎么办?这些思考最终都体现在了芯片选型和外围电路的设计上。下面,我就结合自己画的原理图,把每个部分的设计思路、器件选型考量以及实际布线中要注意的坑,详细拆解一遍。
2. 核心芯片选型与资源分配解析
2.1 主控MCU:STM32F101C8T6的“够用”哲学
选择STM32F101C8T6,根本出发点就是“成本控制”和“资源匹配”。这个项目对MCU的性能要求并不苛刻,不需要复杂的图形界面或高速运算,核心任务是协议解析、数据打包和串口调度。F101系列虽然是F1家族里的“经济型”,但Cortex-M3内核的效率和丰富的外设足以应对。
为什么是LQFP48封装?我选择的是36脚封装(实际是LQFP48,但其中12个是NC空脚,有效36脚)。对于这个项目,引脚数量是完全够用的。两个USART(PA9/PA10, PA2/PA3)是刚需;一个SPI接口(PA5/PA6/PA7)我预留给了SD卡;一个I2C接口(PB6/PB7)作为备用;再加上一些GPIO用于控制GPRS模块的开关机、状态指示,以及RS-485收发器的方向控制,36个引脚绰绰有余。选择小封装直接降低了PCB面积和芯片本身成本。
内存与存储空间评估:
- Flash(64KB):抄表系统的固件,包含了TCP/IP协议栈(我用了轻量级的LwIP或类似简化实现)、数据解析逻辑、设备管理功能等。经过优化后,代码体积可以控制在30-50KB左右,64KB Flash留有充足的余量用于未来功能扩展和OTA升级的缓冲区。
- RAM(20KB):这是需要精打细算的地方。运行轻量级TCP/IP栈、处理网络数据包、缓存串口接收的仪表数据都需要RAM。20KB对于这种单一任务的系统是紧张的,但通过合理的内存池管理、避免大的全局数组、及时释放资源等手段,是可以稳定运行的。这也是没选择更复杂协议栈的原因之一。
两个串口的角色定位:
- USART1 (PA9/PA10):通常作为调试/升级口,或用于连接GPRS模块。我将其用于GPRS模块,因为USART1功能最全,支持硬件流控(RTS/CTS),虽然TC35i不一定需要,但为换用其他模块留了余地。
- USART2 (PA2/PA3):专门用于RS-485通信。STM32的USART输出是TTL电平,需要经过RS-485收发器芯片(如SP3485)转换成差分信号,才能连接长达千米的485总线。
注意:STM32F101的USART2引脚PA2/PA3与调试接口SWD的SWDIO和SWCLK是复用的。这意味着,如果使用了USART2,在进行程序调试(SWD模式)时,这两个引脚会被占用,可能影响485总线的通信。解决方案有两种:一是调试时暂时断开485总线;二是程序初始化时,先完成调试,再重映射引脚功能到USART2。我通常在原理图上会做一个零欧姆电阻的跳线选择,方便调试。
2.2 通信模块:TC35i的经典与务实
TC35i是一款GSM/GPRS Class 8的模块,支持语音、短信、数据和传真。对于抄表,我们只用它的GPRS数据功能。
选型理由:
- 成本极低:在二手市场或拆机件中很容易找到,单价可能只有新型4G模块的十分之一甚至更低。
- 资料成熟:AT指令集公开、稳定,网上有大量的应用笔记和调试经验,开发门槛低。
- 接口简单:主要就是一个串口(TTL电平)、一些控制引脚(PWRKEY, IGTT等)和SIM卡接口,与STM32连接非常简单。
关键电路设计要点:
- 电源供给:TC35i在发射数据时,峰值电流可能达到2A。因此,绝不能使用MCU的LDO为其供电!必须单独一路电源,且电源走线要宽,输入端需要并联大容量(如1000uF)电解电容和多个100nF陶瓷电容去耦,以应对瞬时大电流。
- 启动与关机:TC35i的
PWRKEY引脚需要拉低至少1秒然后释放来开机。在原理图中,我使用一个STM32的GPIO通过一个三极管或MOSFET来控制,实现软件开关机。同时,IGTT(点火信号)通常需要接一个上拉到VCC的电阻。 - 串口连接:直接与STM32的USART1的TX/RX交叉连接即可。务必记得,TC35i是3.3V TTL电平,与STM32电平兼容,无需电平转换。但要注意,TC35i的串口引脚耐压可能不高,最好串联一个22欧姆左右的电阻起限流保护作用。
- 状态指示:
SYNC引脚可以接一个LED,通过其闪烁模式(如每秒闪一次表示注册到网络,快速闪表示GPRS数据活动),可以直观判断模块状态,对于现场调试至关重要。 - SIM卡电路:SIM卡座周边需要ESD保护二极管(如SMF05C),并且SIM卡的
SIM_VCC、SIM_RST、SIM_CLK、SIM_DATA信号线要串联33欧姆电阻并靠近卡座放置。SIM_DATA需要上拉到3.3V。
实操心得:TC35i对电源纹波非常敏感。如果电源质量不好,模块在注册网络或发送数据时极易掉线。实测中,除了电容滤波,在模块的VBAT输入端增加一个磁珠(如600欧姆@100MHz)来抑制高频噪声,能显著提高连接稳定性。另外,给模块的电源路径上增加一个可恢复保险丝(PPTC),可以防止模块短路时殃及整板。
2.3 存储与外设接口的取舍
SPI与SD卡:我使用了STM32的SPI1(PA5/PA6/PA7)连接了一个Micro SD卡槽。SD卡的作用是作为数据缓存器。当GPRS网络不稳定或断线时,采集到的仪表数据不能丢失,需要先存入SD卡,等网络恢复后再逐一补传。SD卡采用SPI模式驱动,代码简单,兼容性好。
为什么不用片内Flash存数据?虽然STM32F101C8T6有64KB Flash,但Flash的擦写次数有限(通常1万次),且以扇区为单位操作,管理复杂。频繁地写入仪表数据(可能几分钟一次)会快速耗尽Flash寿命。而SD卡(特别是工业级)更适合频繁的读写操作。
I2C与EEPROM的预留:我预留了I2C接口(PB6/PB7),但最初设计并未连接EEPROM芯片。原因是STM32的片内Flash可以用来存储一些需要掉电保存、但又不常修改的参数,例如设备ID、服务器IP、APN配置等。使用片内Flash保存参数,可以省下一颗外部EEPROM芯片(如AT24C02)的成本和PCB空间。但是,我仍然把I2C接口的引脚通过排针引出来了。这是一个重要的设计经验:“预留测试和扩展接口”。在后期调试时,我可能临时需要接一个I2C的传感器(如温湿度)来监测设备环境;或者项目升级,需要增加RTC时钟芯片(如PCF8563),而它通常走I2C总线。预留的排针使得这些扩展变得轻而易举,无需改动PCB。
RS-485总线接口:这是与现场仪表通信的桥梁。我选用了一颗常见的3.3V供电的RS-485收发器,如SP3485。设计要点:
- 方向控制:STM32的一个GPIO(如PA1)连接到收发器的
RE(接收使能,低有效)和DE(发送使能,高有效)引脚。发送数据前,拉高DE并拉低RE;发送完毕后,拉低DE并拉高RE,切换回接收状态。 - 总线终端匹配:在485总线的最远端,A和B线之间需要并联一个120欧姆的终端电阻,以消除信号反射。这个电阻最好用跳线或拨码开关连接,方便根据实际布线情况启用或禁用。
- 保护电路:工业现场环境复杂,485总线容易引入浪涌或共模干扰。在收发器的A/B线对地之间,需要加TVS管(如SMBJ6.5CA)进行瞬态抑制,并串联自恢复保险丝。
3. 系统电源树设计与PCB布局要点
3.1 多路电源的生成与隔离
整个系统的电源输入我设计为宽压DC 9-24V,以适应不同的现场供电环境(如12V蓄电池或24V开关电源)。
电源架构如下:
- 第一级:防反接与过压保护。输入端口串联一个二极管防止电源反接烧毁,并并联一个压敏电阻或TVS管吸收高压尖峰。
- 第二级:DC-DC降压。使用一颗降压型开关稳压器(如MP2451),将9-24V输入降至一个中间电压5V。开关电源效率高,发热小,适合作为主功率转换。
- 第三级:LDO稳压与隔离。
- 5V转3.3V (LDO1):为STM32、RS-485收发器、SD卡等数字电路部分供电。选用低噪声的LDO,如AMS1117-3.3。
- 5V转4.0V (LDO2):专门为TC35i模块供电。TC35i的标准工作电压是4.0V,虽然3.3V也能工作,但在信号边缘和最大功率输出时可能不稳定。使用独立的LDO为其供电,可以实现与数字电路的电源隔离,避免GPRS模块发射时的大电流纹波干扰MCU,导致复位或工作异常。
- 3.3V转1.8V (LDO3):如果需要,为SD卡的VDD供电(某些卡需要1.8V低电压模式以获得更低功耗)。
踩坑记录:早期版本我曾尝试用一路大电流LDO同时给MCU和TC35i供电。结果每次模块发起GPRS连接时,MCU都会异常复位。用示波器查看3.3V电源轨,发现上面有高达400mV的瞬间压降。这就是典型的“抢电”现象。后来改为两路独立LDO后,问题彻底解决。教训:对噪声敏感的数字核心电路与射频功率电路,一定要进行电源隔离,即使它们电压相同。
3.2 PCB布局与走线的黄金法则
对于这种混合信号(数字+射频)的板子,PCB布局几乎决定了项目的成败。
1. 分区布局:
- 电源区域:DC-DC开关电源及其电感、输入输出电容应集中放置在一个角落,远离敏感的信号线,特别是模拟和射频部分。
- 数字区域:以STM32为核心,周围放置其晶振、去耦电容、复位电路。SPI、I2C等低速总线器件(如SD卡座)可布置在此区域。
- 射频区域:以TC35i模块为核心。这是重中之重。最好将整个GPRS模块设计成一个可插拔的子板,或者在其周围划定一个“禁区”。
- 天线接口:TC35i的射频天线焊盘(或连接器)到天线座(如SMA头)的走线必须是50欧姆阻抗控制的微带线。如果PCB是1.6mm厚的FR4,线宽大约在3mm左右。这条线要尽量短、直,其下方所有层必须净空(挖掉电源和地平面),周围用接地过孔“围栏”屏蔽。
- 模块下方:TC35i模块底部(如果焊接)对应的PCB区域,所有层(包括地平面)必须全部挖空,防止金属层影响天线性能。
2. 地平面与分割:
- 使用完整的接地层(Ground Plane)是最好的选择。它能提供低阻抗的回流路径,并起到屏蔽作用。
- 关键技巧:单点接地。数字地(DGND)和射频模块的模拟地(AGND)在物理上属于同一个地平面,但需要在某一点(通常选择在电源输入滤波电容的接地端)用磁珠或0欧姆电阻进行“单点连接”。这样可以防止数字噪声通过地平面串扰到敏感的射频电路。TC35i模块的接地焊盘一定要用多个过孔良好地连接到主地平面。
3. 去耦电容的放置:
- 每个芯片的电源引脚附近(越近越好,最好在1mm以内)都必须放置一个0.1uF(100nF)的陶瓷电容,用于滤除高频噪声。
- 在电源进入一块区域的位置(如5V进入数字区),需要增加一个10uF的钽电容或陶瓷电容,用于缓冲中低频的电流需求。
- STM32的去耦:除了每个VDD/VSS对附近的100nF电容,在芯片的电源入口处,还需要并联一个1-10uF的电容。
4. 信号走线:
- 高速线:USB(如果有)、SDIO(如果用到)等走线需要做阻抗控制,并保持等长(差分对)或长度匹配。
- 敏感线:STM32的晶振走线要尽量短,并用地线包围,远离其他高速信号线。
- 电源线:宽度要足够。可以根据电流大小计算,一般1A电流对应至少15mil(0.38mm)的线宽。给TC35i供电的走线,宽度应加倍。
4. 软件框架与关键驱动实现要点
硬件是骨架,软件是灵魂。对于这个抄表系统,软件需要稳定、可靠、易于维护。
4.1 主程序状态机设计
程序不能写成简单的while(1)轮询,那样效率低且难以管理多个异步事件(串口接收、网络事件、定时采集)。我采用了一个基于时间片或事件驱动的状态机框架。
// 简化版状态机示例 typedef enum { SYS_STARTUP, SYS_CHECK_NETWORK, SYS_IDLE, SYS_READ_METER, SYS_SAVE_TO_SD, SYS_GPRS_CONNECT, SYS_UPLOAD_DATA, SYS_ERROR_HANDLE } SystemState_t; int main(void) { // 硬件初始化 HAL_Init(); SystemClock_Config(); UART_Init(); SPI_SD_Init(); GPRS_Init(); // ... SystemState_t state = SYS_STARTUP; while (1) { switch (state) { case SYS_STARTUP: if (Read_Params_From_Flash() == SUCCESS) { state = SYS_CHECK_NETWORK; } break; case SYS_CHECK_NETWORK: if (GPRS_Check_Signal() > 10) { // 信号强度大于10 state = SYS_GPRS_CONNECT; } else { state = SYS_IDLE; Set_Timer(60); // 1分钟后再检查 } break; case SYS_IDLE: if (Timer_Expired()) { state = SYS_CHECK_NETWORK; } if (Meter_Read_Event_Flag) { // 定时采集时间到 state = SYS_READ_METER; } break; case SYS_READ_METER: // 通过485读取电表数据 if (Read_Meter_Data(&meterData) == SUCCESS) { state = SYS_SAVE_TO_SD; } else { state = SYS_ERROR_HANDLE; } break; case SYS_SAVE_TO_SD: if (SD_Save_Data(&meterData) == SUCCESS) { state = SYS_UPLOAD_DATA; } break; case SYS_GPRS_CONNECT: if (GPRS_Attach_Network() && GPRS_Activate_PDP() && TCP_Connect_Server()) { state = SYS_UPLOAD_DATA; } else { state = SYS_IDLE; Set_Timer(300); // 5分钟后重试 } break; case SYS_UPLOAD_DATA: if (SD_Has_Data() && Upload_Data_To_Server() == SUCCESS) { SD_Delete_Sent_Data(); } state = SYS_IDLE; Set_Timer(METER_READ_INTERVAL); // 设置下一次采集定时 break; case SYS_ERROR_HANDLE: // 记录错误日志,尝试恢复 Error_Recovery_Procedure(); state = SYS_IDLE; Set_Timer(60); break; } // 处理串口中断接收到的数据(AT指令响应、485数据) UART_Rx_Process(); // 处理看门狗等 IWDG_Refresh(); } }这种设计清晰地将系统行为划分为离散的状态,每个状态只做一件事,完成后明确地切换到下一个状态。易于调试和维护。
4.2 GPRS通信驱动与AT指令处理
与TC35i通信的核心是发送AT指令并解析响应。这里必须使用中断+环形缓冲区的方式来处理串口数据。
1. 数据接收:在STM32的USART1中断服务函数中,将接收到的每一个字节存入一个环形缓冲区(Ring Buffer)。绝对禁止在中断里进行复杂的字符串解析或判断。
2. 指令发送与响应解析:在主循环或一个专用的任务中,检查是否有AT指令需要发送。发送后,启动一个响应超时定时器。然后,另一个解析函数会不断从环形缓冲区中读取数据,寻找预期的响应字符串(如OK,ERROR,+CREG: 0,1等)。
3. 实现稳健的AT交互流程:
// 示例:发送AT指令并等待特定响应 GPRS_Status_t GPRS_Send_Command_And_Wait(const char *cmd, const char *resp, uint32_t timeout_ms) { UART_Clear_Rx_Buffer(); // 清空接收缓冲区 UART_Send_String(cmd); UART_Send_String("\r\n"); // AT指令以回车换行结束 uint32_t start_tick = HAL_GetTick(); while ((HAL_GetTick() - start_tick) < timeout_ms) { if (UART_Find_String_In_Buffer(resp)) { return GPRS_OK; } if (UART_Find_String_In_Buffer("ERROR")) { return GPRS_ERROR; } // 这里可以执行其他低优先级任务或调用空闲钩子函数 __WFI(); // 进入睡眠,等待中断唤醒,省电 } return GPRS_TIMEOUT; }4. 关键流程:
- 初始化:
AT->ATE0(关闭回显)->AT+CMEE=1(开启详细错误报告)。 - 网络注册:
AT+CREG?查询网络注册状态,直到返回+CREG: 0,1(已注册到本地网络)或0,5(已注册,漫游)。 - GPRS附着:
AT+CGATT=1。 - 设置APN:
AT+CGDCONT=1,"IP","CMNET"(中国移动)。 - 激活PDP上下文:
AT+CGACT=1,1。 - 建立TCP连接:
AT+CIPSTART="TCP","your.server.com","8080"。 - 发送数据:
AT+CIPSEND-> 等待>提示 -> 发送实际数据(以0x1A结束)。
避坑技巧:AT指令的响应时间是不确定的,网络差时可能很长。因此,每个步骤的超时时间要设置得足够长(例如,网络注册设置2分钟,TCP连接设置1分钟)。并且,一定要实现完整的错误处理和重试机制。例如,TCP连接失败后,不能无限重试,应该记录失败次数,达到一定阈值后,尝试重启GPRS模块甚至整个系统。
4.3 数据存储与断点续传策略
SD卡采用FAT32文件系统,使用开源的FatFs库。数据存储策略如下:
- 文件命名:按日期或序号生成文件名,如
DATA_20231027.TXT或DATA_00001.DAT。 - 数据格式:每条记录包含时间戳、设备ID、表计地址、读数、状态等,以固定的格式(如JSON或自定义二进制结构)存储,末尾加换行符。
- 写操作优化:不要每次采集到一条数据就打开、写入、关闭文件。这样会极大损耗SD卡寿命。应该开辟一个内存缓冲区(如512字节),攒够一定数量的记录或定时(如每5分钟)才一次性写入文件。
- 断点续传:在文件中记录一个“发送指针”。每次从SD卡读取数据上传服务器成功后,更新这个指针(可以记录在文件的特定位置,或用一个独立的索引文件
INDEX.DAT来记录)。下次启动时,从指针位置开始读取,实现断点续传,避免数据重复或丢失。 - 文件循环与清理:当单个文件大小超过设定值(如1MB),或文件数量超过一定数量,自动删除最旧的文件,防止SD卡被写满。
5. 调试、测试与常见问题排查
5.1 硬件调试步骤
- 上电前检查:用万用表蜂鸣档检查电源与地是否短路。检查所有芯片的电源引脚电压是否正确。
- 核心供电测试:先不焊接STM32和TC35i,只焊接电源部分。上电,测量各路LDO输出是否准确(3.3V, 4.0V)。
- 最小系统测试:焊接STM32及其最小系统电路(晶振、复位、Boot0/1)。使用ST-Link通过SWD接口连接,尝试烧录一个最简单的LED闪烁程序,验证MCU能否正常工作。
- 串口测试:将STM32的USART1通过TTL转USB模块连接到电脑。编写程序,让USART1定时发送字符串“Hello World”。用串口助手(如XCOM)查看是否收到。
- TC35i模块独立测试:将TC35i模块的
VCC、GND、TX、RX、PWRKEY引出,单独连接到一个USB转TTL模块和单片机开发板(或另一个USB转TTL模块控制PWRKEY)。手动发送AT指令,看能否收到OK响应。这一步至关重要,可以排除模块本身和SIM卡的问题。 - 系统联调:将所有部件焊接好。先调试485通信,连接一个标准的Modbus RTU电表或一个485转USB模块模拟电表,测试数据读取是否正常。然后再调试GPRS联网和数据上传。
5.2 软件调试与日志系统
在项目中集成一个简单的日志系统,通过一个预留的调试串口(可以是USART3,如果引脚够用,或者复用某个IO口模拟串口)输出运行状态、错误信息,是快速定位问题的利器。
#define DEBUG_ENABLE 1 #if DEBUG_ENABLE #define LOG_INFO(fmt, ...) printf("[INFO] " fmt "\r\n", ##__VA_ARGS__) #define LOG_ERROR(fmt, ...) printf("[ERROR] %s:%d: " fmt "\r\n", __FILE__, __LINE__, ##__VA_ARGS__) #else #define LOG_INFO(fmt, ...) #define LOG_ERROR(fmt, ...) #endif // 使用示例 LOG_INFO("System startup..."); if (GPRS_Init() != SUCCESS) { LOG_ERROR("GPRS module init failed!"); }当出现问题(如无法联网)时,查看日志输出,就能清晰地看到程序执行到了哪一步,AT指令交互的具体内容是什么,错误码是多少,极大提升调试效率。
5.3 常见问题与解决方案速查表
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 系统上电不启动 | 1. 电源短路或反接。 2. 3.3V LDO无输出。 3. STM32复位电路问题。 4. Boot引脚配置错误。 | 1. 测量电源输入对地电阻。 2. 测量LDO输入输出电压。 3. 检查复位引脚电压(正常为高),按下复位键应变低。 4. 确保Boot0为低电平(通过10K电阻下拉),Boot1可悬空或下拉。 |
| TC35i无法开机 | 1.PWRKEY引脚时序不对。2. 供电不足或纹波大。 3. 模块损坏或SIM卡问题。 | 1. 用示波器测量PWRKEY引脚,确保有>1秒的低电平脉冲。2. 测量模块VBAT引脚电压,在发射时是否跌落到3.6V以下,加大输入电容。 3. 更换模块或SIM卡测试。 |
| TC35i收不到“OK” | 1. 串口线接反(TX/RX)。 2. 波特率不匹配(默认9600)。 3. 未发送回车换行( \r\n)。4. 模块未开机或处于异常状态。 | 1. 交叉连接TX/RX。 2. 尝试不同波特率(9600, 115200)。 3. 确保指令以 \r\n结尾。4. 检查 SYNC引脚LED状态,尝试给模块重新上电。 |
| 网络注册失败 | 1. SIM卡未插好或欠费。 2. APN设置错误。 3. 当地信号覆盖差。 4. 模块天线未接或损坏。 | 1. 重插SIM卡,确认卡座接触良好。 2. 发送 AT+CGDCONT?查询APN设置,联系运营商确认。3. 发送 AT+CSQ查询信号强度,大于10才可尝试注册。4. 检查天线连接,尝试更换天线。 |
| TCP连接失败 | 1. 服务器IP/端口错误。 2. 防火墙拦截。 3. PDP上下文未激活。 4. 网络运营商限制(如未开通GPRS)。 | 1. 用电脑上的网络调试工具测试服务器端口是否可达。 2. 检查服务器防火墙规则。 3. 发送 AT+CGACT?确认PDP状态为1。4. 确认SIM卡已开通GPRS数据业务。 |
| 485通信不稳定 | 1. AB线接反。 2. 终端电阻未加或阻值不对。 3. 波特率、数据位、停止位不匹配。 4. 总线过长或有强干扰。 | 1. 交换A、B线测试。 2. 在总线两端测量AB间电阻,应为60欧姆左右(两个120欧并联)。 3. 确认与仪表参数一致。 4. 缩短总线,使用双绞线,并检查保护电路。 |
| SD卡无法识别 | 1. SPI引脚接错。 2. 电源不稳(SD卡需要较大启动电流)。 3. 文件系统损坏。 4. 卡座接触不良。 | 1. 检查SCK, MISO, MOSI, CS连接。 2. 在SD卡VCC引脚并联一个100uF电容。 3. 将卡用电脑格式化(FAT32)。 4. 清洁卡座触点。 |
| 系统运行一段时间后死机 | 1. 看门狗未喂。 2. 堆栈溢出。 3. 内存泄漏。 4. 中断冲突或优先级设置不当。 | 1. 检查看门狗初始化及喂狗代码。 2. 加大启动文件中的堆栈大小。 3. 检查动态内存分配,确保成对使用malloc/free。 4. 检查中断嵌套,避免在中断中处理过久。 |
这个基于STM32和TC35i的GPRS抄表终端,从原理图设计到软硬件调试,是一个典型的嵌入式系统开发项目。它涉及了MCU选型、电源管理、射频电路布局、多协议通信(RS-485, GPRS)和稳健的嵌入式软件设计等多个方面。最大的体会是,硬件上的“小气”(如省掉一颗EEPROM)可能会带来软件上的麻烦,而硬件上的“大方”(如预留测试点、增加电源隔离)则会为后期的调试和稳定性带来巨大便利。对于这种工业环境使用的设备,稳定性永远是第一位的,宁可前期多花时间在设计和测试上,也不要等到批量部署后再去现场解决问题。
