1. 项目缘起与整体设计思路按键一按短信发出OLED屏幕上同步刷新发送状态——这个需求听起来简单但真正动手做过的朋友都知道里面藏着不少门道。我最近刚完成一个基于STM32和Air780E的短信发送终端核心功能就是通过物理按键触发把预设的中文短信内容发送到指定手机号同时在0.96寸OLED屏幕上实时显示模块状态、发送进度和结果反馈。整个项目从选型到调试花了大约一周时间踩了不少坑也积累了一些在常规文档里找不到的经验。先说说这个项目适合谁看。如果你正在做基于STM32的毕业设计或者想入门蜂窝通信模块的开发又或者你手头正好有Air780E和一块OLED屏想折腾点实用的东西那这篇内容应该能帮你省下不少查资料和试错的时间。整个方案不依赖复杂的云平台也不需要额外的服务器就是最朴素的“单片机通信模组显示屏”的组合硬件成本控制在百元以内软件层面用HAL库驱动代码结构清晰方便二次修改。为什么选Air780E而不是其他模组这是我在方案选型阶段重点考虑的问题。市面上常见的Cat.1模组有不少选择Air780E的优势在于它支持中文短信的PDU模式发送AT指令集相对规范而且社区资料比较丰富。更重要的是它的功耗控制做得不错在空闲状态下电流可以降到毫安级别对于需要长时间待机的场景比较友好。OLED屏我选的是SSD1306驱动的0.96寸I2C接口版本四针接线简单显示效果清晰关键是驱动代码成熟网上能找到大量参考例程。整体设计思路是这样的STM32作为主控负责按键扫描、OLED显示刷新和AT指令的收发控制Air780E通过UART与STM32通信负责实际的短信发送和网络注册OLED通过I2C接口连接用于显示当前状态。按键采用外部中断方式检测避免轮询占用CPU资源。短信内容预先存储在STM32的Flash中发送时通过PDU编码转换为Air780E可识别的格式。整个流程分为几个阶段模块初始化、网络注册、短信发送、状态反馈每个阶段都有对应的OLED显示内容。这个方案最大的好处是把复杂的通信过程封装成了简单的按键操作用户不需要了解AT指令的细节只需要按一下按键就能完成短信发送。同时OLED的实时反馈让整个系统的状态一目了然调试起来也方便很多。下面我会从硬件连接、软件架构、AT指令交互、PDU编码、OLED显示这几个维度逐一拆解把每个环节的关键点和踩过的坑都讲清楚。2. 硬件选型与连接细节2.1 核心器件清单与选型理由先列一下我实际使用的物料清单方便大家对照准备。主控用的是STM32F103C8T6最小系统板也就是常说的“蓝板”价格便宜资源够用HAL库支持完善。通信模组是Air780E开发板注意要买带SIM卡座和天线接口的版本否则还得自己飞线。显示屏是0.96寸SSD1306 OLEDI2C接口四针脚定义分别是VCC、GND、SCL、SDA。按键用的是普通的6x6轻触开关加一个10kΩ上拉电阻和100nF消抖电容。另外还需要一张已经开通短信功能的SIM卡这个很重要有些物联网卡默认关闭短信功能需要提前确认。这里重点说一下Air780E的选型考量。它支持LTE Cat.1 bis向下兼容GSM但在实际使用中我建议直接锁定LTE网络因为2G网络在很多地区已经退网或信号很差。Air780E的AT指令集兼容性不错特别是短信相关的指令基本遵循3GPP TS 27.005标准。它的供电范围是3.4V到4.2V典型值3.8V这一点要特别注意不能直接接5V否则可能烧毁模块。我一开始用STM32的5V引脚供电结果模块反复重启后来换成3.3V稳压输出才稳定下来。OLED屏的选择相对简单SSD1306驱动芯片的0.96寸屏在市面上很常见I2C地址通常是0x78或0x7A具体要看模块背面的标注。我用的这块是0x787位地址左移一位后就是0xF0。分辨率是128x64足够显示几行状态信息。需要注意的是有些OLED模块自带稳压芯片和电平转换可以直接接5V但为了保险起见我还是统一用3.3V供电。2.2 接线方案与电源处理接线方面STM32和Air780E之间通过UART通信我使用的是USART2波特率设置为115200。为什么选115200而不是9600因为Air780E的默认波特率就是115200而且高速率可以减少AT指令交互的等待时间。接线时注意TX和RX要交叉连接STM32的TX接Air780E的RXSTM32的RX接Air780E的TX。另外Air780E的PWRKEY引脚需要拉低一段时间来开机我直接用STM32的一个GPIO控制上电后拉低1秒再拉高模块就会启动。OLED的I2C接口我接在STM32的PB6和PB7上这是I2C1的默认引脚。I2C速率设置为400kHz这个速率对于128x64的屏幕来说足够快刷新一屏内容大概只需要几毫秒。按键接在PA0上配置为外部中断模式下降沿触发。这里有个细节要注意PA0同时也是WKUP引脚如果之前用过待机模式可能需要先禁用WKUP功能否则会有干扰。电源处理是这个项目里最容易被忽视但又最关键的部分。Air780E在发送短信的瞬间电流会突然增大峰值可能达到2A左右。如果电源供应不足模块会直接掉电重启导致发送失败。我的解决方案是用一块LM2596降压模块把12V输入降到4V再经过一个大电容滤波后给Air780E供电。STM32和OLED则用另一路3.3V稳压输出。这样分开供电可以避免模块发射时的电流波动影响单片机运行。实测下来在发送短信时用示波器观察电源纹波控制在50mV以内就比较稳定了。注意千万不要用STM32板子上的3.3V稳压器给Air780E供电那个稳压器的输出电流通常只有几百毫安带不动模块的峰值电流。我一开始就是这么干的结果模块每次发送短信就重启排查了半天才发现是供电问题。3. 软件架构与核心模块实现3.1 整体软件流程设计软件部分我采用前后台架构主循环负责状态机和OLED刷新中断负责按键检测和UART数据接收。为什么不跑RTOS因为这个项目的任务并不复杂用状态机就足够了而且可以避免RTOS带来的额外复杂度和资源开销。整个软件分为几个模块按键驱动、OLED驱动、UART驱动、AT指令解析、PDU编码、状态机控制。主循环的状态机有以下几个状态IDLE空闲、INIT初始化、REGISTERING网络注册中、READY就绪、SENDING发送中、SUCCESS发送成功、FAIL发送失败。每个状态对应不同的OLED显示内容和处理逻辑。按键中断触发后如果当前状态是READY就切换到SENDING状态开始发送流程。发送完成后根据结果切换到SUCCESS或FAIL状态并在一段时间后自动回到IDLE状态。UART接收我用了DMA加空闲中断的方式这样可以避免频繁中断影响主循环。具体做法是开启UART的DMA接收然后在空闲中断中计算接收到的数据长度把数据拷贝到缓冲区后重新启动DMA接收。这种方式对于不定长的AT指令响应特别有效不会丢数据。AT指令的解析我用了一个简单的状态机根据预期的响应内容逐行匹配比如发送ATCGREG?后期待收到CGREG: 0,1或CGREG: 0,5表示注册成功。3.2 OLED显示驱动与界面布局OLED驱动我基于SSD1306的官方数据手册自己写了一套精简版的驱动没有用现成的图形库因为那些库通常比较庞大而且很多功能用不上。核心函数就几个OLED_Init()、OLED_Clear()、OLED_ShowString()、OLED_ShowChinese()、OLED_Refresh()。显示缓存我开了一个1024字节的数组对应128x64的点阵所有绘制操作先写到缓存里最后统一刷新到屏幕这样可以避免闪烁。中文字库是个麻烦事。SSD1306本身不带中文字库需要自己取模。我用的方法是把需要显示的中文字用PC端软件取模成16x16的点阵数组然后存到STM32的Flash里。这个项目里需要显示的中文不多主要是“发送中”、“成功”、“失败”、“注册中”这几个词取模工作量不大。如果你需要显示更多中文建议用SPI Flash外挂字库芯片或者用GB2312字库文件按需读取。界面布局我设计得比较简洁第一行显示网络状态比如“已注册”或“注册中”第二行显示当前操作状态比如“就绪”或“发送中”第三行显示发送结果比如“成功”或“失败”第四行显示信号强度用简单的柱状图表示。这样的布局在0.96寸屏幕上刚好能放下字体用16x16的中文和8x16的英文数字混合排版。刷新频率我控制在200ms一次太快了没必要反而增加CPU负担。3.3 按键处理与消抖策略按键处理看似简单但要做好并不容易。我一开始用轮询方式在主循环里检测按键电平结果发现偶尔会误触发。后来改成外部中断加软件消抖效果就好多了。具体做法是PA0配置为下降沿触发中断中断服务函数里启动一个10ms的定时器定时器到期后再检测一次按键电平如果还是低电平就确认按键有效否则认为是抖动。这个10ms的延时是通过定时器实现的不会阻塞主循环。还有一个细节是按键的长按和短按区分。这个项目里我只用了短按功能但如果你需要长按发送不同短信可以在定时器中断里累加按键按下的时间超过1秒就认为是长按。另外按键释放后要清除中断标志位否则会反复触发。我在调试时遇到过按键按一次触发多次的情况后来发现是中断标志没有正确清除加上__HAL_GPIO_EXTI_CLEAR_IT()就好了。实操心得按键两端并联一个100nF的电容可以硬件消抖效果比软件消抖更直接。但电容不要太大否则按键响应会变慢。我试过用1uF的电容结果按键要按下去半秒才有反应后来换成100nF就刚刚好。4. AT指令交互与PDU编码实战4.1 Air780E初始化流程与关键AT指令Air780E上电后需要经过一系列初始化步骤才能正常发送短信。我整理了一个完整的初始化流程每一步都有对应的AT指令和预期响应。首先是开机拉低PWRKEY引脚至少500ms然后等待模块返回RDY。这个RDY是模块启动完成的标志通常需要等3到5秒。如果一直等不到RDY可能是供电不足或者PWRKEY时序不对。开机成功后先发送AT测试通信是否正常预期返回OK。然后发送ATE0关闭回显这样后续的响应会更干净方便解析。接着发送ATCPIN?查询SIM卡状态预期返回CPIN: READY。如果返回CPIN: SIM PIN说明SIM卡有PIN码锁需要先发送ATCPIN密码解锁。再发送ATCSQ查询信号质量返回的第一个数值是信号强度范围0到31越大越好99表示无信号。网络注册是最关键的一步。发送ATCGREG?查询GPRS注册状态返回CGREG: 0,1或CGREG: 0,5都表示已注册。如果返回0,2表示正在搜索0,3表示注册被拒绝0,4表示未知。注册被拒绝的原因可能是SIM卡欠费、APN设置错误或者网络覆盖问题。我遇到过SIM卡没开通短信功能导致注册被拒的情况换了一张卡就好了。短信相关配置需要设置短信格式为PDU模式发送ATCMGF0返回OK。然后设置短信中心号码发送ATCSCA8613800100500这个号码是北京地区的短信中心其他地区需要查询当地运营商的短信中心号码。设置完成后就可以发送短信了。4.2 PDU编码原理与中文短信实现PDU模式是发送中文短信的关键因为Text模式不支持中文。PDU编码的格式比较复杂我花了不少时间才搞明白。一条完整的PDU串包含短信中心号码、接收方号码、协议标识、编码方式、有效期、用户数据长度和用户数据内容。对于中文短信编码方式要设置为UCS2也就是UTF-16BE编码。以发送“你好”到手机号13800138000为例PDU串的构造过程是这样的首先短信中心号码8613800100500去掉加号后是8613800100500前面加上91表示国际格式再补上长度和F得到0891683108100005F0。接收方号码13800138000前面加86补F对齐后是8613800138000F0长度是0D。协议标识用00编码方式用08表示UCS2有效期用AA表示默认。用户数据长度是4个字节“你好”两个汉字每个汉字2字节十六进制是04。用户数据内容是“你好”的UCS2编码你好的Unicode码点是4F60597D所以PDU串就是0891683108100005F011000D91683110830000F00008AA044F60597D。发送时用ATCMGS长度指令长度是PDU串中除去短信中心号码部分的字节数。然后等待模块返回提示符再发送PDU串并以CtrlZ0x1A结尾。模块会返回CMGS: 消息参考号表示发送成功或者CMS ERROR表示失败。在STM32上实现PDU编码我写了一个函数把手机号和中文内容转换成PDU串。手机号的处理相对简单就是加86补F。中文内容需要先转成UCS2编码STM32的HAL库没有现成的Unicode转换函数我是用查表法实现的把常用的汉字和对应的Unicode码点做成一个数组用的时候查表。如果汉字数量多建议用GB2312到Unicode的转换表或者直接用UTF-8编码然后转UCS2。注意PDU串的长度计算容易出错特别是用户数据长度字段。这个字段是用户数据的字节数不是字符数。一个汉字在UCS2编码下占2个字节所以“你好”的长度是4不是2。我一开始算错了导致模块返回错误排查了好久才发现是长度字段的问题。4.3 发送流程与状态反馈完整的发送流程是这样的按键触发后STM32先检查当前是否处于READY状态如果是就切换到SENDING状态OLED显示“发送中”。然后构造PDU串发送ATCMGS长度等待模块返回提示符。收到提示符后发送PDU串和CtrlZ等待模块返回CMGS或CMS ERROR。根据返回结果更新状态为SUCCESS或FAILOLED显示对应内容。最后等待3秒自动回到IDLE状态。等待模块响应的时候要注意超时处理。我设置了10秒的超时时间如果10秒内没有收到预期响应就认为发送失败OLED显示“超时”。这个超时时间可以根据网络状况调整信号好的时候5秒就够了信号差的时候可能需要15秒。超时后要发送ESC0x1B取消当前操作否则模块会一直等待输入。状态反馈方面除了OLED显示我还加了一个LED指示灯。发送中LED慢闪成功常亮失败快闪。这样即使不看屏幕也能知道发送结果。LED接在PC13上这是STM32F103C8T6板载的LED不用额外接线。5. 常见问题与排查技巧实录5.1 模块不响应或反复重启这是最常见的问题我遇到过好几次。首先检查供电用万用表量一下模块的VCC引脚正常应该在3.8V左右。如果电压低于3.4V模块肯定工作不正常。然后检查PWRKEY时序用示波器看拉低时间是否足够500ms。如果都正常再检查UART接线TX和RX有没有接反波特率是不是115200。还有一个容易被忽视的问题是SIM卡接触不良。Air780E的卡座是推拉式的有时候卡没插到底就会接触不良。我遇到过模块能开机但一直注册不上网络的情况后来把SIM卡拔出来重新插紧就好了。另外SIM卡的方向也要注意缺角朝里芯片朝下。5.2 短信发送失败错误码解析模块返回CMS ERROR时会带一个错误码常见的错误码和原因我整理了一个表错误码含义排查方向1无网络检查信号强度确认已注册3操作不允许SIM卡可能未开通短信功能10SIM卡未插入检查卡座接触11需要PIN码发送ATCPIN解锁21短信中心号码错误用ATCSCA?查询当前设置28无服务确认网络注册状态38网络超时重试或检查信号50编码错误检查PDU串格式我遇到最多的是错误码3和21。错误码3通常是SIM卡问题换卡或者联系运营商开通短信功能。错误码21是短信中心号码设置错误不同运营商的号码不一样移动是8613800100500联通是8613010112500电信是8613800100500。设置的时候要注意加号不能省略。5.3 OLED显示异常排查OLED不亮或者花屏也是常见问题。首先检查I2C地址用逻辑分析仪抓一下I2C波形看有没有应答。如果没有应答可能是地址错了SSD1306的地址通常是0x78或0x7A具体看模块。如果地址对了但不亮检查初始化序列SSD1306需要发送一系列命令才能正常显示包括设置对比度、显示模式、扫描方向等。花屏的问题我遇到过两次。一次是因为I2C速率太高400kHz下偶尔会丢数据降到100kHz就好了。另一次是因为显示缓存没有清空残留的数据导致花屏。解决方法是在每次刷新前先清空缓存或者用OLED_Clear()函数。还有一点要注意OLED的VCC和GND要接好接触不良也会导致花屏。实操心得如果OLED显示中文时出现乱码大概率是字库取模的方式不对。SSD1306的显示缓存是按页组织的每页8行取模时要按页取不能按行取。我一开始按行取模显示出来的中文全是乱码后来改成按页取模就正常了。6. 项目扩展与优化方向这个项目的基础功能已经跑通了但还有很多可以优化的地方。比如可以增加短信接收功能收到短信后在OLED上显示可以增加多个按键每个按键对应不同的短信内容可以加一个RTC时钟在短信内容里带上时间戳还可以把发送记录存到Flash里方便查询。功耗优化也是一个方向。目前模块一直处于在线状态功耗在几十毫安左右。如果改成定时唤醒比如每小时唤醒一次检查有没有新短信功耗可以降到毫安以下。具体做法是用STM32的RTC定时唤醒唤醒后给模块上电注册网络检查短信然后断电。这样用电池供电可以撑好几天。代码结构方面我建议把AT指令的发送和解析封装成独立的函数用回调的方式处理响应。这样主循环的逻辑会更清晰也方便移植到其他模块上。比如可以定义一个AT_SendCmd()函数传入指令和预期响应返回成功或失败。超时和重试机制也封装在里面调用的时候只需要关心业务逻辑。最后分享一个小技巧调试AT指令的时候可以用USB转TTL模块直接连接Air780E在电脑上用串口助手手动发送指令确认指令格式和响应都正确后再写到STM32代码里。这样可以大大减少调试时间避免在单片机上反复烧录。我在开发初期就是这么做的先用串口助手把整个发送流程跑通然后把指令序列复制到代码里一次就成功了。