ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

STM32驱动NB-IoT模组BC95:从AT指令到低功耗实战

STM32驱动NB-IoT模组BC95:从AT指令到低功耗实战 简介一套面向STM32嵌入式开发者的BC95-G NB-IoT模块驱动源码包适合需要在MCU上快速接入窄带物联网通信的工程师与学习者。资源体积仅4KB包含一份C源文件和一份头文件前者实现初始化、数据收发、回调注册等核心逻辑后者定义结构体、常量与函数原型结构简洁便于直接移植到基于HAL库或LL库的工程中。驱动封装了BC95_Init、BC95_SendData、BC95_ReceiveData及BC95_RegisterCallback等接口可帮助开发者屏蔽底层串口时序细节通过AT命令完成网络注册与数据交互。已有255人学习下载对正在调试STM32串口中断、超时与错误处理或希望以最小改动实现NB-IoT设备联网的开发者有直接参考价值。配合BC95模块官方AT手册可快速完成远程监控、智能设备联网等低功耗物联网项目的原型验证与二次开发。 上个月在做一套智能水表的数据上报方案主控选了 STM32L431通信模组用的是移远 BC95。选型理由其实很现实项目要求电池供电、设备装在管道井里、至少运行五年不用换电池LoRa 得自己建网关4G Cat.1 的功耗又扛不住NB-IoT 几乎是这个场景下唯一能兼顾网络覆盖和功耗的选项。当时以为驱动开发就是对着 AT 指令集写几个串口发送函数真正动手才发现BC95 这颗模组要把 AT 指令跑得稳定、跑得低功耗、跑得不出幺蛾子底下全是细节。这篇东西不是官方文档的翻译也不是照抄例程的笔记。我根据自己从零开始写 STM32 侧 BC95 驱动程序的完整过程把硬件连接、代码分层、AT 指令流程、调试方法、低功耗调优这几个最关键的环节都梳理一遍。适合正在做 NB-IoT 终端、准备用 BC95 或者同级 NB 模组、但还没系统捋过驱动架构的工程师参考。已经跑通 Demo 的人重点看第三章和第五章的代码组织方式与排查链路里面有些坑是我实测踩出来的。1. 为什么是 BC95NB-IoT 场景下的选型分析1.1 BC95 这颗模组到底什么定位BC95 是移远基于海思 Hi2110 芯片方案做出来的 NB-IoT 模组工作在授权频谱上支持 B1/B3/B5/B8/B20 这些主流 LTE 频段。它的定位非常明确低速、低功耗、广覆盖、海量连接。单连接上行速率理论峰值在 60kbps 左右实际跑业务数据通常也就几 kbps 到十几 kbps但这对于抄表、传感器上报、停车位检测这类场景完全够用。模组本身通过 UART 接口接收 AT 指令内部报文走的是 3GPP 标准协议栈对 MCU 来说它就是一个串口透传的 IP 终端。这意味着驱动工作的本质其实有两层第一层是让 STM32 能可靠地把 AT 指令发出去、把模组响应收回来第二层是搞清楚 BC95 特有的 AT 指令时序和 NB-IoT 网络状态转换把入网、注册、收发数据这些流程封装成应用层能直接调用的函数。1.2 选型之前先算清楚账功耗、速率与成本有些刚接触 NB-IoT 的开发者会有个误区觉得 NB-IoT 什么都好结果项目做到一半发现速率不够或者时延扛不住。我列一张实际的对比表方便你判断 BC95 到底适不适合自己的项目。对比项BC95 (NB-IoT)4G Cat.1LoRa峰值下行速率~60kbps~10Mbps~50kbps视配置功耗PSM 模式3-5uA 级别1mA 级别2-5uA 级别网络基础设施运营商基站运营商基站自建网关覆盖范围广深覆盖好广受网关位置限制模块成本较低较高中等适用业务低频小包上报语音、视频、高频交互私有网络控制BC95 最大的优势在于 PSM省电模式和 eDRX 这两个特性。PSM 模式下模组进入类似关机的睡眠状态但网络侧会保留它的注册上下文醒来后不需要重新附着网络直接就能发数据。实测下来如果业务是每天上报一包几十字节的数据平均功耗可以压到很低的水平这就是为什么水表、气表这类设备喜欢用 NB-IoT。驱动开发的复杂度其实和这些特性直接相关。你想用好 PSM就必须在驱动里正确配置ATCPSMS、处理唤醒时序、设计合理的发送窗口。如果只是拿官方例程跑通一次 UDP 收发就交差项目落地后功耗大概率会翻车。这也是我后面专门用一章写低功耗调优的原因。2. 硬件连接里最容易翻车的三个细节2.1 电平匹配1.8V 串口不是 3.3VBC95 的 UART 电平是 1.8V不是 3.3V也不是 5V。这个细节在数据手册里写得很清楚但很多人第一步就栽在这里我也一样。第一次上电测试时我直接把 STM32 的 TX 引脚连到 BC95 的 RXD结果模组完全没有响应AT 指令石沉大海。查了半天用万用表量电平才发现 BC95 的 UART 引脚高电平只有 1.8V而 STM32 的输出高电平是 3.3V逻辑虽然能识别但长期这样直连对模组引脚有损伤风险而且 STM32 的 RX 引脚接收 1.8V 信号时未必能稳定识别高电平。正确做法是加电平转换电路。常见方案有两种一是用 TXB0102 这类双向电平转换芯片二是用两个 MOS 管搭简单电路。如果手头有现成的电平转换模块优先用芯片方案稳定性和速率都有保障。BC95 串口默认波特率 9600速率不高MOS 管方案也能用但要注意上拉电阻的选取。我实际量产板卡上用的是 TXB0102电路就是标准的 A/B 两端口接法和 VCCA/VCCB 分别接 1.8V 和 3.3V没有额外处理。驱动跑起来后串口数据没有出现乱码或丢字节的情况。如果你的板子上没有 1.8V 电源可以用 STM32 的一个 GPIO 配置成开漏输出加上拉电阻到 1.8V或者用 LDO 单独供 1.8V。千万别为了省一个 LDO 就把 3.3V 直接怼到模组上。2.2 上电时序PWRKEY 的坑BC95 的 PWRKEY 引脚不是高电平使能而是拉低触发上电。具体时序是给模组供电后等待 VBAT 稳定然后把 PWRKEY 拉低至少 500ms再释放模组才会开机。很多开发者习惯性地认为使能脚就是拉高我最初也这么干过结果模组上电后串口始终没反应后来翻手册里 Figure 上电时序图才意识到问题。驱动里我把这个流程做成了专门的BC95_PowerOn()函数先保证电源稳定延时 200msGPIO 拉低 PWRKEY延时 600ms释放 PWRKEY接着等待模组返回RDY或者^MODE: 0这类上电指示。这里有个小技巧如果上电后模组没有主动输出任何字符可以先手动发送一次AT正常情况下会收到OK或AT回显说明模组已经活了。另外 PWRKEY 上最好加一个 10k 下拉电阻避免悬空误触发电平抖动。量产板还要注意如果 MCU 先上电、模组后上电模组开机瞬间 GPIO 如果处于高阻态下拉电阻能保证 PWRKEY 保持在高电平不会意外开机。2.3 天线和 SIM 卡影响稳定的隐蔽因素天线和 SIM 卡这两个东西看起来跟驱动没关系但我在调试中遇到的不少诡异问题最后都指向它们。BC95 的 RF 引脚需要接 NB-IoT 频段的天线如果天线没接或者匹配不好模组能开机、能发 AT 指令但ATCSQ查信号会返回 99表示无信号或者注册网络永远不成功。排查这类问题的时候先别急着改代码用ATCSQ测一下信号强度是最快的验证手段。SIM 卡这里有两个坑。第一BC95 用的是标准 SIM 卡接口但模组对 SIM 卡的电压要求是 1.8V/3V如果卡座供电异常会导致 SIM 卡无法识别ATCPIN?返回 ERROR。第二NB-IoT 业务卡和普通物联网卡有区别有些卡虽然插上能读到 IMSI但没开通 NB-IoT 套餐或者没有在运营商侧绑定设备入网附着就会一直失败。这个我在第五章会详细讲排查过程。硬件上建议 SIM 卡座靠近模组放置走线不要太长SIM_DATA 上加上拉电阻通常 10k-22k卡座外围加 ESD 保护器件。这些是硬件工程师的活儿但驱动联调出现问题时你得能判断出是卡的问题还是软件的问题。3. 驱动代码怎么分层从 UART 抽象到 AT 指令状态机3.1 最底层串口接收用 DMA空闲中断驱动设计的第一步是把 STM32 的 UART 抽象成一个健壮的收发通道。BC95 的响应是典型的不定长字符串发一条指令后模组可能回一行、也可能回多行最后以OK或ERROR结尾。用最原始的逐字节中断接收然后拼字符串在低速场景下也能跑但代码效率低、容易丢数据而且无法处理模组主动上报的数据例如NSONMI:通知。我推荐的做法是 DMA 接收 串口空闲中断IDLE Line Interrupt。思路是开启 UART 的 DMA 接收让硬件自动把收到的字节搬进环形缓冲区同时开空闲中断。当串口总线上出现一段静默时间即一个字节的时间没有新数据硬件判定当前帧接收完成触发空闲中断这时 DMA 收到的数据就是一整包完整的模组响应。代码结构上用 HAL 库实现大概是这样以 STM32L4 为例// 定义接收缓冲区 #define BC95_RX_BUF_SIZE 1024 uint8_t bc95_rx_buf[BC95_RX_BUF_SIZE]; uint8_t bc95_dma_buf[BC95_RX_BUF_SIZE]; // 配置 DMA 接收 HAL_UART_Receive_DMA(huart2, bc95_dma_buf, BC95_RX_BUF_SIZE); // 空闲中断回调在 HAL_UART_IRQHandler 中触发 void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart) { if (huart-Instance USART2) { // 计算本次实际接收长度 uint16_t len BC95_RX_BUF_SIZE - __HAL_DMA_GET_COUNTER(hdma_uart2_rx); // 把数据拷贝到环形缓冲区 ring_buf_write(bc95_rx_ring, bc95_dma_buf, len); // 重新开启 DMA 接收 HAL_UART_Receive_DMA(huart2, bc95_dma_buf, BC95_RX_BUF_SIZE); } }这里的关键点是DMA 接收必须配置成循环模式空闲中断触发后要重设 DMA 计数器否则第二次接收就会出现问题。另外要注意 HAL 库的空闲中断回调函数名可能因为版本不同而有差异我用的是HAL_UART_IdleCpltCallback如果你的 HAL 版本没有这个函数直接在HAL_UART_IRQHandler里用__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE)判断然后清标志、手动处理。这样底层就有两个核心资产一个环形缓冲区存原始数据一个解析线程可以放在主循环里轮询也可以用 RTOS 的队列从缓冲区里按行读取内容。3.2 AT 命令层状态机超时AT 指令通信的核心不是发出去而是等对响应。BC95 在响应 AT 指令时可能先回一段前置信息例如^MODE: 0再回最终结果OK、ERROR。如果并发或频繁发指令响应更可能交错。所以驱动不能写成简单的发指令 - 阻塞等待 - 读一字节这种线性流程一旦响应延迟稍长整条链路就卡死了。我采用的方案是一个简单的有限状态机typedef enum { AT_STATE_IDLE, AT_STATE_WAIT_RESP, AT_STATE_TIMEOUT, } at_state_t; typedef struct { at_state_t state; char cmd_buf[128]; char resp_buf[512]; uint16_t resp_len; uint32_t tick_start; uint32_t timeout_ms; } at_handle_t;发送流程是主调函数填充cmd_buf写入 UART TX然后把状态转成WAIT_RESP记下当前 tick。解析任务持续从环形缓冲区取数据逐行判断如果这一行是OK状态变回IDLE返回成功如果这一行是ERROR返回失败如果超时返回超时错误否则继续累积到resp_buf。这个状态机的好处是其他业务代码不会被一条慢指令阻塞。例如查询信号强度ATCSQ如果网络忙可能要几百毫秒才返回如果直接HAL_UART_Transmit后阻塞等待整个主循环都会卡住。用状态机之后发完指令可以先干别的活等到响应回来了再通知业务层。3.3 业务接口给应用层一个干净 API底层和 AT 命令层是通用的真正跟 BC95 业务相关的是封装出来的 API。我建议把这些接口独立成一个文件比如bc95_driver.c/h对外暴露这样几个核心接口uint8_t BC95_Init(void); // 上电、查询模组版本、配置APN uint8_t BC95_GetSignal(int8_t *rssi, int8_t *ber); // 查询信号强度 uint8_t BC95_GetAttachState(uint8_t *state); // 查询附着状态 uint8_t BC95_CreateUDPSocket(uint8_t *sock_id); // 创建UDP socket uint8_t BC95_SendUDP(uint8_t sock_id, char *ip, uint16_t port, uint8_t *data, uint16_t len); uint8_t BC95_ReceiveUDP(uint8_t sock_id, char *ip, uint16_t *port, uint8_t *data, uint16_t *len); void BC95_EnterPSM(void); // 配置PSM应用层不需要关心 AT 指令长什么样只需要调用这些函数。比如上报数据的代码最终就长这样uint8_t sensor_buf[64]; int16_t rssi; uint8_t attach_state; // 确保已经入网 BC95_GetAttachState(attach_state); if (attach_state ! 1) { BC95_Attach(); } // 查询信号强度判断当前是否适合发送 BC95_GetSignal(rssi, NULL); if (rssi 3) { // 信号太差先不入网过段时间再试 return ERR_SIGNAL_WEAK; } // 填充传感器数据 sprintf((char *)sensor_buf, {\t\:25.3,\h\:60.2,\v\:3.7}); // 发送UDP数据 BC95_CreateUDPSocket(sock_id); BC95_SendUDP(sock_id, 183.230.40.40, 5683, sensor_buf, strlen((char *)sensor_buf)); BC95_CloseSocket(sock_id);这样划分之后底层串口怎么收发、AT 指令怎么解析对上层完全透明。后面即使换一颗模组比如换 BC35-G 或者移远的其他 NB 模组大部分 AT 指令是兼容的需要改的只是少数厂商私有指令不会牵一发动全身。4. 核心 AT 指令流程入网、附着、Socket 收发的完整时序4.1 上电-复位-查询模组状态模组上电后不能急着发业务指令必须确认它已经准备就绪。推荐的启动序列是AT— 返回OK确认串口通信正常。ATE0— 关闭回显减少串口数据量可选但推荐。ATI— 查询模组固件版本确认模组型号和固件是否符合预期。ATCFUN1— 设置射频功能为全功能开启。ATCFUN1这一步容易被忽略但很重要。模组出厂可能处于飞行模式CFUN0这时候射频是关闭的信号查询、网络附着都会失败。驱动初始化时一定要把ATCFUN1加上并且确认返回OK。4.2 入网附着SIM 卡、频段、注册初始化完成后先检查 SIM 卡ATCPIN? CPIN: READY OK返回READY说明 SIM 卡识别正常。如果返回ERROR或CME ERROR: SIM not inserted先检查卡是不是没插好或者卡座虚焊。接下来配置频段。这是 BC95 比较特殊的地方不同运营商的 NB-IoT 网络使用不同的频段例如中国移动主要用 B8中国电信主要用 B5中国联通主要用 B3/B8不同地区和时期有差异。模组默认可能是全频段扫描但在信号复杂的现场锁定单频段能缩短搜网时间、提升成功率。ATNBAND8 OKBC95 的ATNBAND参数含义8对应 B8 频段5对应 B5 频段3对应 B3 频段。锁定后需要复位模组设置才会生效ATNRB OK然后是 APN 配置。NB-IoT 卡一般有专用的 APN例如中国移动的 NB 卡 APN 通常是cmnbiot联通是cmiot电信是ctnb。驱动里用ATCGDCONT1,IP,cmnbiot配置。如果 APN 不对即使信号满格也无法正常收发数据。接着查询网络注册状态ATCEREG? CEREG: 0,1 OKCEREG第二位的含义是关键1表示已注册到 NB-IoT 网络可能是归属网络5表示已注册但处于漫游状态0表示未注册2表示正在搜索网络3表示网络拒绝注册4表示未知。只有1或5才说明模组已经在网可以继续后面的收发操作。最后查附着状态ATCGATT? CGATT: 1 OKCGATT: 1表示已附着到分组网络。驱动里我习惯把BC95_Attach()封装成轮询ATCEREG?和ATCGATT?直到状态满足条件或者超时。超时时间我设置成 60 秒因为模组冷启动搜索网络需要时间尤其在地下管道井这类弱信号环境几十秒才附着成功是正常现象。4.3 UDP 收发NSOCR/NSOST/NSORFBC95 的 IP 层通信使用扩展 AT 指令和普通的 TCP/IP 模组如 ESP8266风格很不一样。最常用的 UDP 收发调用序列如下创建 UDP SocketATNSOCRDGRAM,17,5683,1 0 OK参数含义分别是DGRAM表示数据报类型17是 UDP 协议号5683是本地端口号1表示收到数据被动上报。返回的0就是 socket ID。发送数据ATNSOST0,183.230.40.40,5683,30,7B2264223A7B7D7D OKATNSOST的第 4 个参数是发送数据的长度字节数第 5 个参数是十六进制表示的数据内容。这里有个很大的坑BC95 不接收裸字符串的数据必须先把要发送的 ASCII 字符串转成十六进制字符串。比如要发hello实际要填的是68656C6C6F。驱动里我专门写了一个hex_encode()函数做这个转换稍后会说。接收数据分两种情况。如果创建 socket 时第 4 个参数设为 1自动上报模组收到数据后会主动推送NSONMI: 0,12表示 socket 0 收到了 12 字节数据。然后执行读取指令ATNSORF0,20 NSORF: 0,183.230.40.40,5683,12,12345678901234567890,0 OK返回内容中第 4 个字段是数据长度第 5 个字段是数据内容同样是十六进制字符串最后一个0表示缓存中还有没有更多数据。如果你创建的 socket 第 4 个参数是 0那么收到数据后系统会有缓存但不会主动上报需要通过轮询ATNSORF来取。收发完成记得关闭 socketATNSOCL0 OK整个流程看下来其实很简单但实际操作中要注意每次ATNSOST发送的数据长度不能超过模组的 MTUBC95 的 UDP 单包最大长度一般是 1024 字节实际上 NB-IoT 网络上层往往只有几百字节的保障建议单包控制在 512 字节以内。另外频繁地创建和关闭 socket 会增加网络开销也容易触发模组内部资源紧张。我实测下来同一个 socket 建议复用只发数据不关闭等到业务层明确不需要了再关。5. 调试实录从完全连不上到稳定上报5.1 排查链路串口日志 → CSQ → 注册状态 → Socket如果整个系统无法通信我建议严格按照这个链路排查不要跳步。第一件事永远是确认串口通没通复位模组看看上电后有没有RDY输出手动发一条AT看有没有回OK。如果连这一步都过不了先回头查电平转换电路、TX/RX 是不是接反了、波特率是不是 9600。第二步查信号。发ATCSQ返回值第一个数表示接收信号强度范围 0 到 3199 表示无信号。举个例子ATCSQ CSQ: 12,0 OK这个值是 12换算成 dBm 大约在 -105dBm 左右属于能入网但信号一般的水平。如果长时间在 0 到 2 徘徊或者直接返回99就要考虑换天线、改变设备摆放方向实在不行换测试地点。第三步查 SIM 卡和注册状态用ATCPIN?和ATCEREG?。我遇到过一次ATCEREG?一直返回0,3网络拒绝注册换了另一张同运营商测试卡就好了说明是卡被运营商侧停用或欠费。这类问题很容易被怀疑成模组硬件故障其实跟代码一点关系都没有。第四步查 Socket 通信。先用ATPING指令测一下服务器 IP 是否通ATNPING183.230.40.40 NPING: 0,183.230.40.40,10,0 OK返回的第二段是 RTT 毫秒数第三段是丢包数0代表通。如果连续超时先检查服务器 IP 和端口是否可达再看是不是 NB 网络的 UDP 限制有些运营商对非标准端口有拦截策略。5.2 信号强度与天线摆放实验我在信号问题上的教训很深。最初把设备放在实验室桌面上ATCSQ永远在 5 左右附着成功率不到一半我还以为是驱动代码写错了。后来把天线旁边的金属支架挪开、把天线从水平摆放改成垂直朝上ATCSQ直接从 5 跳到 15附着几乎秒成功。实测经验是NB-IoT 天线非常敏感周围 5cm 内的金属物体、大面积覆铜、甚至人体靠近都会明显影响信号。在设备结构设计时尽量让天线伸出外壳或者至少保证天线区域净空。调试过程中用延长线把天线放到不同位置测一遍ATCSQ能找到当前环境下的最优位置。这一步很笨但对项目落地帮助极大。5.3 频段设置与运营商网络的匹配这个坑我再讲一遍因为碰到的人真的很多。某次在测试现场模组能注册但不能收发数据ATCEREG?返回0,1注册成功ATCGATT?也是1但发 UDP 数据就是没有响应。查网络资料才发现模组锁定的是 B5 频段但现场基站是 B8 频段注册时靠多频段扫描碰巧成功数据通道却一直异常。驱动里一定要允许运行时修改频段不能写死在代码里。我提供了一个配置接口BC95_SetBand(uint8_t band)内部调用ATNBANDx然后ATNRB复位再重新初始化。这样在现场遇到运营商标识不明的情况可以把几个候选频段都试一遍快速定位问题。另外NB-IoT 卡和网络也有归属问题。比如移动的 NB 卡在网络侧是独立的分组域APN 如果填错即使注册状态是 1也会出现无法建立数据承载的情况。排查这种注册成功但数据不通的问题时重点检查 APN 是否和运营商一致。6. 低功耗优化PSM 和 eDRX 的驱动侧实现6.1 PSM 模式配置与唤醒机制NB-IoT 的功耗优势主要来自 PSM。进入 PSM 后模组射频收发机关闭但保留在网络的注册信息电流能降到 3-5uA 级别。驱动里配置 PSM 的核心指令是ATCPSMS1,,,00100100,00000101 OKATCPSMS各参数含义第一个1表示启用 PSM引号里第一个值可空是周期性 TAU跟踪区更新定时器第二个值和第三个值分别是T3324活动时间定时器和T3412扩展周期 TAU 定时器这两个值编码成 8 位二进制每 3 位表示一个时间单位。简单解释T3324 表示模组空闲多长时间后进入 PSM单位有秒、分、时等T3412 表示模组最长多久必须醒来一次做 TAU 更新确保网络侧会话不失效。我实测设置成00100100T33242 分钟和00000101T3412≈2 小时时设备每天上报一次数据的平均功耗可以做到很低。配置后查询ATCPSMS? CPSMS: 1,,,,,00100100,00000101 OK确认生效即可。但要注意PSM 是否真的进入驱动侧没法直接看到只能通过电流表观察。如果配置后电流没降下来一种可能是模组还保持着 socket 没有关闭另一种可能是业务层频繁唤醒模组发数据导致它一直停留在 Active 状态。6.2 实测功耗数据和调度建议我拿电流分析仪实际抓过一组数据。BC95 在 PSM 睡眠态大约是 4.2uA唤醒后进入空闲态已注册网络但没传输数据大约是 1.8mA发送数据瞬间峰值能到 250mA 到 350mA取决于信号强度和发射功率。如果不做任何低功耗设计模组常驻空闲态每天 24 小时功耗大约在几十毫安时级别开启 PSM 后每天只有两次短暂唤醒一次 TAU 更新、一次业务上报实测单日平均电流可以控制在几十微安级别一节大容量锂电池跑几年确实可以做到。驱动侧的调度策略建议是这样的设备上电后完成初始化然后立即入网入网成功后马上进入 PSM 睡眠。当应用层需要上报数据时比如定时器到期或传感器触发先唤醒模组快速发完数据然后立刻重新进入 PSM。整个过程中驱动要把所有 AT 指令的执行时间控制住不要发一条执行 3 秒、发下一条又等 5 秒这些时间累加起来会让模组一直保持在活跃状态。有一个细节要注意PSM 模式下模组醒来可能来不及立刻收下发数据因为网络侧下发数据需要先寻呼模组模组要维护 PSM 里的寻呼监听窗口。如果服务器主动下发数据给设备必须等设备主动上行数据后打开接收窗口才能收到或者提前用云平台配置成设备上行后立刻下行否则数据会一直积压在网络侧。因此驱动里不建议使用长连接、大包交互这类设计业务协议应该尽量做成一问一答式的短交互。最后分享一个调试小技巧BC95 进入 PSM 后串口发送 AT 指令是不会响应的因为 UART 也被内部关掉了。要唤醒模组最简单的方式是通过 PSM 唤醒源比如定时器或者把 PWRKEY 拉低一次触发复位。如果你在调试中遇到发 AT 没反应先别急着怀疑程序看看模组是不是已经睡死过去了。判断方法是量一下模组 VBAT 的电流几 uA 级就是睡着了重新唤醒即可。本文还有配套的精品资源点击获取
返回列表