FEATURED · 精选文章

BLE与Flash擦写冲突排查:BlueNRG掉线问题的互斥方案

发布时间 / 2026/8/30 23:56:39
来源 / 创域科博编辑部
栏目 / 资讯中心
BLE与Flash擦写冲突排查:BlueNRG掉线问题的互斥方案 前一阵调试一个基于BlueNRG-2的蓝牙外设功能本身不复杂通过BLE接收配置参数写进内部Flash下次上电再加载。单测一切正常一接到手机App做连续配置写入连接就开始频繁掉线用抓包器看是Supervision Timeout。一开始怀疑天线匹配或者供电不稳后来用逻辑分析仪把Flash擦写引脚和蓝牙事件引脚叠在一起看才发现掉线时间和Flash擦写高度重合。换句话说不是射频不行是Flash操作和BLE协议栈在抢同一颗核的时间。这个问题的本质在BlueNRG-1/BlueNRG-2这类单芯片方案里特别典型BLE协议栈和应用代码跑在同一颗Cortex-M核上没有RTOS托底你先天的就要自己管理谁可以先占用CPU、谁可以等。这篇笔记就是把我在这个项目里的排查过程、几种互斥方案、以及最终落地的写入状态机完整梳理一遍。内容包括BLE连接事件的时间模型、Flash擦写耗时的真实量级、为什么简单关中断是个坑、以及怎么用GPIO实测来证明方案真的生效。不管你是正在调BlueNRG-1还是已经换了BlueNRG-2只要涉及BLE运行中写内部Flash这个场景应该都能用得上。1. 冲突的根源Flash擦写时间与BLE事件窗口的硬碰撞1.1 单核上跑BLE协议栈和Flash驱动天然就是一对矛盾BlueNRG-1和BlueNRG-2都是把BLE射频前端、链路层控制器、协议栈和应用处理器集成在一颗芯片里。对开发者来说这是一颗SoC但对实时性来说本质上是一个单核处理器要在同一个时钟周期里既处理射频中断又跑你的业务循环。Cortex-M0/M0本身没有内存管理单元也没有硬件任务切换BLE协议栈的很多关键路径依赖中断来保证时序。射频包进出、基带事件调度、加密引擎、定时器校准这些都在中断上下文里完成。而你要做Flash擦写时Flash控制器处于busy状态CPU取指和访问Flash都要等待如果代码本身就是从Flash里取出来的擦写期间的指令流会被拉长甚至直接stall。就算你把Flash驱动放到RAM里跑中断能否及时响应也取决于Flash控制器是否阻塞了总线仲裁。所以互斥在蓝NRG这颗芯片上不是一个普通的临界区问题。两个任务之间不是一个持有锁另一个等一会儿的关系而是一个正在读写Flash时另一个无线事件已经到时间点必须被服务。你没法让BLE事件停下来等它只认超时。这个矛盾是硬件结构决定的不是靠软件技巧能绕开的。1.2 一个数据手册才能告诉你的时间账擦除耗时不是瞬间很多第一次接触内部Flash的开发者会低估Flash操作的时间。读Flash是微秒级甚至纳秒级但擦除和写入完全是另一个量级。以BlueNRG系列常见的内部Flash为例单页擦除的时间通常在10到20毫秒这个范围按页写入program虽然比擦除快很多但完整写一页数据也需要若干毫秒。不要拿写单个字只要几十微秒来推算整页时间控制器要对整页做充电和校验过程不是线性的。具体数值必须看你手上型号的参考手册不同型号的页大小和擦除时间不一样。BlueNRG-1和BlueNRG-2的Flash容量、页大小、写保护机制都有差别同一个API在不同型号上的实际耗时也可能不同。做设计时千万别用应该很快来假设要用手册里的典型值和最坏值来算后面在压测里我们还会提到这一点。这10到20毫秒听起来不长但放在BLE连接事件里已经是能错过一整个连接事件的长度了。常规连接间隔是30到50毫秒一个连接事件里真正用来收发包的窗口也就1到2毫秒。一次页擦除等于把一个连接事件整体挤掉如果业务逻辑连续擦好几页链路层就会连续错过多个事件最终触发Supervision Timeout。1.3 我现场遇到的三个典型故障场景我这边的项目实际出现过三种表现排查方向完全不同但根源都是Flash和BLE抢时间。第一种是连接一段时间后手机端提示设备断开抓包显示Supervision Timeout。这种情况通常出现在周期性的数据存储任务里比如每5分钟写一次日志或者每次收到配置后立刻回写Flash。只要你写Flash的时间点撞上了连接事件链路就可能丢一个事件。一次两次还扛得住连续写入时协议栈还没来得及补状态马上又撞下一次超时计数就归零了。第二种是广播中的设备偶尔连不上。广播事件不像连接事件那样要求每个事件都被应答所以很多人认为广播期间写Flash没问题。但实际上如果擦写时间点和广播事件重叠广播包发送会被延迟手机端的扫到信号强度会忽高忽低有时候要重试好几次才能建链。对于需要快速配网的场景这种问题非常隐蔽因为它不报错只表现为连接慢。第三种是我一开始没想到的Flash里的数据写坏了。排查后发现不是写坏而是写的时间不对——蓝牙协议栈某些版本使用同一块存储区维护绑定信息和NVDS参数如果你在协议栈正在更新NVDS的时候去擦它管理的扇区两边互相覆盖数据校验自然就过不去。这类问题不是逻辑错误是资源冲突的连锁反应。2. 先把BLE的时序账算清楚再决定怎么写Flash2.1 连接事件、广播事件和空闲期BLE在时间轴上到底怎么排的BLE的链路层把时间划分成严格的事件窗口。连接状态下主设备和从设备约定一个连接间隔Connection Interval默认可以短到7.5毫秒常见应用会设到30到50毫秒。每个连接间隔开始后双方在同一个物理信道上交换数据包交换完或者时间窗用尽双方都进入休眠直到下一个间隔开始。这个交换数据包的时间窗就是连接事件实际占用的时长取决于双方有多少数据要发。广播状态更宽松一些。广播事件按照广播间隔周期性地发比如20毫秒一次或者100毫秒一次中间有大量的空闲时间。但因为广播包没有确认机制错过了就错过了手机端只能等下一个广播事件才能收到。所以BLE在时间轴上的真实样子是周期性出现的小窗口连接事件或广播事件中间夹着大片空闲。理想情况下Flash操作应该塞进空闲区但问题是你并不知道空闲区什么时候结束。更麻烦的是连接事件的实际长度不是固定的取决于收发队列里有没有数据、对端是否回复、射频重传是否发生。所以在空闲期写Flash不是一个可以精确计算出来的安排而是需要一个能和协议栈协同时序的调度机制。2.2 主循环模型下应用代码的可阻塞窗口BlueNRG的SDK里典型应用是一个大循环先处理BLE协议栈事件再处理自己的应用任务然后再回到协议栈。某些版本叫BTLE_Stack_Process()有些叫HCI_Process()核心都是把协议栈的待处理任务推进一轮。协议栈内部的事件调度靠中断驱动但应用层代码在主循环里串行执行。这意味着你的App_Process()不能长时间占着CPU不放。你每占用一个较长的时间片协议栈的中断就会排队射频基带响应就会出现延迟。这个延迟如果超过链路层允许的范围轻则丢包重则断链。那么可阻塞窗口到底有多长没有标准答案。它取决于你的连接间隔、连接事件长度、以及对端主设备的调度策略。但有一条经验我可以直接给你单个操作超过3到5毫秒就已经需要认真审视了。这不是说绝对不能超过而是说一旦超过你就要对BLE时序负责要有明确的避让手段。很多开发者就是在这里踩的坑——认为自己就擦写一下几毫秒而已结果在高负载连接下就崩了。2.3 用余量反推安全窗口到底能堵多久如果要在连接状态下写Flash你至少需要算清楚三笔账第一是你当前连接配置下的事件间隔。假设连接间隔30毫秒那么每个间隔里你有大约28毫秒的空闲但这些空闲不是全部都能用因为连接事件本身可能因为重传被拉长你还得给协议栈留足够的中断响应时间。第二是超时预算。Supervision Timeout是主从双方判断链路是否存活的时间阈值常见设400毫秒到几秒。链路层允许你错过若干连接事件但只要连续错过的时间超过超时值连接就断了。所以理论上你可以错过几个事件但这个预算不可再生一个超时周期内错过的事件次数是有限的。第三是最坏情况下的Flash操作时间。一次擦除10到20毫秒写一页又是若干毫秒如果整个存储流程需要擦除整块写多页校验总耗时可能到50到100毫秒。这个时间不仅会错过一两个连接事件还可能在边界情况下触发超时。我的建议是把安全窗口定义为连接间隔中扣除连接事件最大长度按最坏情况估计和协议栈中断处理所需的余量后剩下的可用时间。如果Flash总耗时超过这个窗口就说明你不能直接写必须先调整连接参数或者拆分成多次小写入。3. 五种互斥方案从避开到协商的取舍3.1 方案A连接断开时再写逻辑最简单但不是所有业务都能等最简单的互斥是没连接的时候才写。设备在广播状态、或者完全没有连接的情况下BLE对时间的要求比连接状态低很多你可以在主循环里放心地做一次完整的Flash写入。这个方案的优点是代码量几乎为零不用处理连接参数也不用担心时序。缺点是业务上经常等不起。以我的项目为例用户通过手机改完配置期望立刻生效比如调整设备名称、修改上报周期如果非要等断开连接才写体验上完全不可接受。而且有些设备是常连的可能一整天都不断开。所以方案A适合的场景是写Flash的时机本身可以延迟到断开之后比如记录一些只在本地上报、不依赖手机端确认的统计数据。如果业务要求写完成后再回状态给手机这个方案就不合适。3.2 方案B临时拉长连接间隔写完成后再恢复这是处理连接状态下做长时间Flash操作最直接的手段。思路是在真正写Flash之前先向对端请求一个更长的连接间隔比如从30毫秒临时改成100毫秒甚至更长。间隔拉长后每个连接事件之间的空闲时间显著增加Flash操作就有足够的窗口去执行。等Flash写入完成再请求恢复原来的连接参数。实现上需要用到连接参数更新机制。如果你这边是主设备可以直接用HCI层的Connection Update命令如果是外设则需要通过L2CAP的连接参数更新请求把这个请求发给对端等对端接受或者拒绝。对端同意后你会收到连接更新完成事件之后再进行Flash操作这个顺序很重要不能发完请求立刻写。风险在于对端不一定接受你的参数更新请求。有些中心的协议栈策略比较保守可能会拒绝过大的间隔调整或者只接受一个折中值。所以方案B不能做成发完请求就等结果的死脑筋逻辑还要考虑请求失败的回退路径。3.3 方案C按页拆分写入把擦除塞进连接事件间隙如果Flash操作总量不大比如只写几十个字节理论上可以拆成擦一页、写一个字、擦一页、写一个字的节奏让每一次长时间操作都落进连接事件的间隙里。这个方案对连接几乎无感不需要改参数也不依赖对端配合。但难点在于如何知道连接事件的间隙在哪。应用层拿不到链路层精确的事件调度信息你只能靠当前没有协议栈事件需要处理这种粗粒度信号来判断。我在实际的工程里见过有人用定时器估算记录上一个事件处理完成的时间在下一个预计事件到来之前做Flash操作但因为连接事件长度不固定这个方法在重传场景下会出错。这个方案更适合单个页擦除时间明显小于连接间隔的场景并且要求你能容忍一定的时序估计误差。它不适合大数据量写入因为你要写的页多了拆分的编排逻辑会非常复杂而且每一段写入之间的状态维护成本也不低。3.4 方案D直接用官方持久化库把调度问题交给库如果你只需要存储用户配置、绑定信息这类小数据强烈建议先看看官方SDK里的存储库而不是自己写Flash驱动。BlueNRG的SDK里提供了一套持久化存储相关的服务库内部会处理擦写均衡、掉电保护以及一部分和BLE时序相关的协同逻辑。你调用库的接口去写数据库自己决定什么时候真正操作Flash。用官方库的好处是把是否安全、何时安全这个判断从应用层抽离出去。你不用自己维护状态机也不用关心页大小和擦除时间。代价是灵活性降低比如你可能无法精细控制写入时机也无法拿到底层的Flash错误状态。另外不同SDK版本里存储库的API差异比较大升级SDK时要注意兼容性。我的建议是凡是可以不用直接操作Flash的就不要直接操作。写Flash这件事底层细节比表面看起来多得多交给被广泛测试过的库能省掉不少半夜出bug的麻烦。3.5 方案ERAM缓冲 写请求状态机保证任何时刻只有一件事在动Flash这算是我目前工程里最终采用的组合思路。核心思想是把写Flash从一个函数调用变成一个分阶段的状态机。业务层永远不直接调用Flash擦写函数而是先往RAM缓冲区里丢数据然后发出一个写请求状态机在合适的时机把RAM里的数据落盘落盘完成后回一个完成事件。这套设计的关键在于Flash的打开和关闭、擦除和写入、还有和BLE协议的交互都被收敛进一个状态机里。任何时刻只有这个状态机能触碰Flash业务代码和BLE协议栈都不能同时操作Flash。从另一个角度看这才是真正的互斥——不是靠关中断不是靠等待而是从软件架构上杜绝了并发访问的可能。3.6 五个方案怎么选一张表讲清楚我做个对比表方便你快速判断方案对连接的影响实现复杂度依赖对端配合适合场景A. 断开再写无最低否本地统计、可延迟的配置存储B. 临时拉长间隔低但需恢复中是对端需接受参数更新需要在线完成大块写入C. 拆分写入很低较高否小数据量、高频但短时的存储D. 官方存储库由库保证低否小数据、配置类、绑定信息E. 状态机互斥可控较高可选项目里有多处写入来源、需要统一管控实际项目中E方案通常会和A、B、C中的某个配合使用。比如我的项目就是主状态机用E方案统一管理写入当检测到有活动连接且需要大面积更新时才临时走B方案拉长间隔如果连接已经断开则走A方案直接写写的内容不多时又切到C方案的节奏。五个方案不是互斥的而是组合拳。4. 可落地的参考实现基于连接参数调整的写入状态机4.1 状态机的状态定义与切换条件我最终实现的状态机大概长这样。先定义几个状态WR_IDLE没有写入请求Flash完全空闲。WR_WAIT_PARAM已经收到写入请求正在等待连接参数更新完成。WR_DOING正在执行Flash擦除/写入/校验此时所有BLE数据下发都暂停。WR_RESTORE_PARAM写入完成正在请求恢复原来的连接参数。WR_FAIL写入失败或者参数更新被拒绝进入错误处理。切换逻辑的核心是从WR_IDLE到WR_DOING之间必须经过一个安全窗口确认。如果当前没有连接那么确认条件直接满足如果有连接则必须等待协议栈返回连接参数更新完成事件才能进入WR_DOING。状态机的触发入口是一个flash_write_request标志。业务模块想写Flash时只置这个位然后立刻返回。真正写Flash的动作放在主循环的应用任务里逐状态推进保证不会在中断上下文里执行Flash擦写。4.2 关键代码参数调整、写入执行与参数恢复下面是这套流程的示意代码API名称以你手上的SDK版本为准重点看流程结构。/* 状态机变量 */ static volatile uint8_t flash_state WR_IDLE; static volatile uint8_t flash_write_pending 0; static uint8_t flash_scrach[CFG_DATA_LEN]; /* 业务侧请求写入只做标记不直接操作Flash */ void App_Request_Flash_Write(uint8_t *data, uint16_t len) { if (flash_state ! WR_IDLE) return; /* 忙就丢弃或排队由业务决定 */ memcpy(flash_scrach, data, len); flash_write_pending 1; } /* 主循环里推进状态机 */ void App_Flash_StateMachine(void) { switch (flash_state) { case WR_IDLE: if (flash_write_pending) { if (g_ble_connected) { /* 有连接先拉长连接间隔确保后续写入有时间窗口 */ aci_l2cap_connection_parameters_update_req( g_conn_handle, 80, 80, 0, 800); /* 100ms间隔1s超时 */ flash_state WR_WAIT_PARAM; } else { flash_state WR_DOING; /* 无连接直接写 */ } flash_write_pending 0; } break; case WR_WAIT_PARAM: /* 等待 EVT_LE_CONNECTION_UPDATE_COMPLETE 事件 在事件处理函数里把状态切到 WR_DOING */ break; case WR_DOING: BSP_Flash_WriteAll(g_cfg_addr_in_flash, (uint8_t *)flash_scrach, sizeof(flash_scrach)); flash_state WR_RESTORE_PARAM; break; case WR_RESTORE_PARAM: if (g_ble_connected) { aci_l2cap_connection_parameters_update_req( g_conn_handle, g_orig_min_int, g_orig_max_int, g_orig_latency, g_orig_timeout); } flash_state WR_IDLE; break; } }事件处理函数里要处理连接参数更新完成的事件void Process_BLE_Event(uint8_t *event) { uint16_t evt_code event[0] | (event[1] 8); if (evt_code EVT_LE_CONNECTION_UPDATE_COMPLETE) { if (flash_state WR_WAIT_PARAM) { flash_state WR_DOING; /* 参数已更新可以安全写入 */ } else if (flash_state WR_RESTORE_PARAM) { flash_state WR_IDLE; /* 参数已恢复状态机结束 */ } } }这里有几个容易被忽略的细节。第一EVT_LE_CONNECTION_UPDATE_COMPLETE同时携带status字段如果对端拒绝了参数更新状态非0此时不能进入WR_DOING应该走错误分支。第二拉长连接间隔后要确保从设备侧的应答策略能适应新间隔尤其是Supervision Timeout要大于新间隔值否则协议栈自己会认为链路超时。第三状态推进发生在主循环不要把它放在某个硬件中断里避免和协议栈中断抢优先级。4.3 恢复与异常处理对端拒绝改参数怎么办我实际调测时最常遇到的异常就是对端拒绝连接参数更新。手机端的BLE协议栈策略多种多样有的允许Peripheral主动更新参数有的会忽略或者拒绝。如果你发出参数更新请求后迟迟等不到完成事件状态机会卡死在WR_WAIT_PARAM这时候就得有超时兜底。做法是在状态机里加一个等待计数进入WR_WAIT_PARAM时启动一个软件定时器比如200毫秒内没等到更新完成事件则不再等待直接把状态切到WR_DOING。为什么还要继续写因为万一参数更新实际已经被对端处理了只是事件丢失或时序上没送到你一直等下去反而更危险。当作窗口已经打开处理然后在WR_DOING里做好Flash操作的失败检查如果写入时不满足条件就返回错误由上层决定重试或延后。如果写入本身失败比如Flash校验不一致我的做法是保留RAM中的数据副本不清理flash_write_pending同时置一个flash_write_error标志业务层看到这个标志后可以执行重试或者上报错误。掉电恢复的问题在状态机层面也有一点考虑每次写Flash前先把目标扇区区做成已擦除但不写数据的中间态数据校验通过后再提交有效标志否则上电恢复时认为上一次数据无效回退到默认参数。5. 验证互斥是否生效抓窗口、压测与常见坑5.1 用GPIO和逻辑分析仪把时序画出来不要只靠看起来没掉线来验证。互斥方案做没做对最好的证据是时序图。我一般在代码里放两个测试用GPIO一个在Flash擦写开始时拉高、结束时拉低另一个在BLE连接事件处理函数里翻转。逻辑分析仪采样率不用太高10MHz足够看ms级时段。两个波形叠在一起看理想情况是Flash busy的高电平永远落在BLE事件的低电平区间。如果发现Flash高电平中间夹着BLE翻转说明还有重叠方案没完全生效。这时候再结合实时抓包看是否丢包就能确定问题的严重程度。如果你的协议栈不提供连接事件处理回调可以用另一个办法把BLE的射频活动引脚配置成复用功能输出很多芯片支持把无线电状态映射到某个IO口这样逻辑分析仪上能看到真实的事件窗口比软件回调更准确。具体引脚要看芯片手册的Alternate Function表。5.2 边写Flash边跑数据流的压测方法验证方案能否在真实场景下工作我要做三类压测。第一类是连续配置写入。模拟用户频繁改配置每收到一次写请求就立刻触发一次Flash写入连续执行200到500次同时通过BLE连接持续接收数据。重点观察写请求之间是否发生断开、GATT响应是否超时、Flash里的数据是否每次都是一致的。第二类是大数据块写入时叠加周期性通知。设备每30毫秒向手机发送一包传感器数据同时后台持续做Flash分页写入。这种情况下连接事件窗口会被数据通知拉长Flash操作能用的间隙更短最能暴露方案里时序估算不足的问题。第三类是低电量模拟。Flash擦写是电流尖峰比较明显的操作如果供电不足擦写过程中掉电会导致Flash处于半擦除状态。我用可调电源把电压压到接近规格下限再触发写入核对掉电恢复后Flash里的状态是否符合预期。这个测试和BLE互斥不是一回事但它决定了你在互斥方案里要不要额外加掉电保护逻辑。5.3 调试时容易误判的几个点我踩过几个坑罗列出来供你排查时参考。第一个是关全局中断保护Flash擦写。这个做法在小MCU裸机程序里很常见但在BLE芯片上行不通。关闭PRIMASK后BLE协议栈的时序基准会乱掉即使Flash擦写成功恢复中断后协议栈状态也可能已经异常表现为调试时一切正常、集成测试时偶发断链。Flash控制器通常有自己独立的busy标志你不需要用关闭全局中断去保护擦写过程需要保护的是访问Flash寄存器的临界区那段时间极短几微秒而已。第二个是收到连接更新完成事件后才认为安全。这个线程安全的直觉是对的但要注意连接更新完成后第一个新间隔的连接事件可能还没发生如果你立刻在这个间隔里做擦写依然可能和紧随而来的第一个事件重叠。稳妥的做法是在连接更新完成事件后再等一个完整的连接间隔再开始Flash操作。第三个是用Flash的实测平均耗时做判断。Flash擦写的耗时有温度、电压、工艺差异平均值和最大值可能差不少。我用同一批芯片在常温下测过擦除时间波动可以达到好几毫秒。设计窗口的时候必须按最坏值算不能按平均数据算。6. 实战经验补充写给正在调Flash的开发者6.1 Flash的平均耗时没有意义要看最坏情况前面反复提到最坏情况这里再展开说。BLE时序预算和Flash耗时的关系本质上是一个硬实时问题。平均耗时50毫秒不代表最坏情况是55毫秒可能因为温度降低、供电电压偏低、芯片批次差异某个角落的芯片擦除一次就到80毫秒。如果你的窗口只按平均值留了10毫秒余量出差到低温现场就会偶发断链这种问题在实验室里很难复现。所以我建议在规格设计阶段就按典型值100%来做Flash操作的时间预算或者直接在代码里把Flash操作前后的时间戳打出来收集一批实际数据看分布再决定预留多少余量。6.2 写入校验和掉电恢复比避开BLE更重要互斥只解决时间上不冲突的问题不解决写入过程中掉电的问题。BLE设备很多是电池供电电池低压瞬间Flash擦写正好进行到一半的情况并不罕见。你不光要考虑擦写是否破坏了BLE时序还要考虑擦写后数据是否完整、掉电后能不能恢复。我的建议是至少做到三点写前把原始数据完整保存在RAM的备份区擦写完成后逐字节校验写入前先擦除一块独立的状态标志区数据完全写成功后再把标志置为有效。这样即使擦写中途掉电上电后也能根据标志判断上一次操作没完成主动回退到默认配置或者请求重传配置。这套逻辑和BLE互斥没有直接关系但它决定了你的存储方案在长生命周期产品里到底稳不稳。6.3 不同SDK版本的API和库行为有差异写这个状态机的时候我基于的SDK版本和我最早看的版本之间连接参数更新接口的名字都变过。BlueNRG的SDK迭代过程中某些API从aci_l2cap_connection_parameters_update_req改名、改参数结构、改事件返回方式的情况都有。最坑的是官方存储库在不同版本里的内部行为差异很大比如有的版本会自动处理BLE写入窗口有的版本则完全交由你调用时机决定。所以不要拿网上的Demo代码直接复制粘贴一定要对着你本地SDK的头文件确认API签名。更关键的是升级SDK后要重新跑一遍5.2节里的三类压测哪怕你应用代码一行没改库内部的行为变了时序特征就会变。6.4 我坚决不碰的几种写法最后说几个我在项目评审时会直接拉黑的写法。一是在BLE中断回调里直接调用Flash擦写API。不管官方API文档写得多轻量Flash擦写的时间量级摆在那里中断上下文里跑一次就是几十毫秒这等于把整个协议栈的实时性毁掉。二是用一个全局大锁把BLE协议栈处理和Flash写入串行化。比如在主循环里处理完协议栈事件后立刻把所有中断关掉跑Flash写完再开中断。这种写法确实做到了互斥但对连接稳定性的破坏比不互斥还严重。三是对Flash操作的结果不做任何检查。Flash擦写会受供电和温度影响而失败尤其电池供电的设备忽略返回值等于埋雷。至少要做一次读回校验校验不过就进入异常处理而不是假装写入成功。四是把互斥逻辑分散在多个业务模块里。每个模块都判断现在是不是安全判断标准还不一样最后一定会在某个组合场景下出现两个模块同时写Flash的情况。我最终改成统一状态机就是因为曾经在三个业务模块里各写了一份保护逻辑结果互相踩踏反而比不做更乱。互斥逻辑只应该存在一个地方其他模块都通过请求和回调来用这是我从这个项目里拿到的最实在的教训。这些内容是我在一个真实产品调试过程中一点点磕出来的不同项目、不同SDK版本、不同连接参数下具体数值和API可能有出入但Flash操作必须与BLE事件在时间上互斥这件事是绕不开的。如果你的设备也出现一写Flash就掉线的怪问题希望这篇笔记能帮你少走几段弯路。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻