AI时代坚持古法手搓客户需求讨论出就是一句话按键5S蓝牙开可发现广播新平板可连接60s超时回连旧设备需求拆解蓝牙身份地址没有强制要求变跟旧平板需要超时回连说明有状态回退常规流程配对上后只有定向广播除非用户强制主动打破状态流转商业开发需要涉及到为不懂技术的人演示一定要做到所见即所得PPT的示例图就是最后的效果以后有争议就以立项时的PPT为主越形象越好给客户留下的印象更深客户看我们的东西就和我们到商场买衣服一样只关心感受方便以及值不值会关心技术够不够高端不会关心后面怎么做的为什么说这么多。远超平均价格的产品对于大众来说就像奢侈品就像是什么非洲高级原木做的梳子1. 质量与安全顾虑 (品质恐惧)心理预期高端客户默认“一分钱一分货”。潜意识反应太低的价格会引发客户怀疑产品使用了劣质原材料、技术落后或制造工艺粗糙。潜在风险担心产品使用寿命短、维护成本高甚至出现安全隐患这对于追求品质生活的客户是不可接受的。2. 服务与售后保障 (隐性成本)重视体验相比单次购买高端客户更看重长期的服务体验。忧虑售后极低的价格通常意味着厂家压缩了利润空间进而压缩了售后服务的质量。客户担心在需要支持时无法得到及时的响应产生“购买后被抛弃”的恐惧。3. 品牌与身份认同 (社会信号)品牌溢价高端客户购买的产品往往是其身份、地位和品位的象征。品牌价值太便宜的产品不仅无法提升形象反而可能降低身份。他们购买的是高价格背后所代表的品牌积淀、社会声誉和稀缺性。4. 信任与确定性 (避险心理)决策成本高端客户更愿意花高价购买一种“确定性”即不用担心产品翻车、不会浪费精力和时间去处理故障。反向恐惧过低的价格会让他们觉得“这事儿太不踏实了”因此宁愿选择价格合理、知名度高、风险可控的方案。总结对于高端客户销售策略不应是“低价诱惑”而应是“价值展现”。要强调产品的专属性、高品质保证、完善的售后以及能带来的增值服务。现在的消费电子早就超过了电子的功能属性而是消费品的属性特别是能触达女性/幼年群体的还要务必向化妆品的产品一样做有吸引力的设计。也就是功能属性的基本可以忽略不计有时候苦哈哈coding无法理解方案设计因为只是按键开新广播先画个草图正常状态退出直接进入广播流程rtos设计application taskprocess优先级stack/ramresponse timeOS serviceBLE taskReset state中64200msButton task按键状态检测高3250msble event callback一般由原厂提供ble状态机维护低128os basic service 很多时候没有被考虑到但是作为平台基础能力别忘了有时候供应商代码完成度高还好各种系统服务都有如果没有集成我们还要自己去移植这部分工作也要在前期考虑进去后续客户需求变更要添加一个LED客户需求按键5S蓝牙开可发现广播新平板可连接60s超时回连旧设备LED显示蓝牙状态、充电状态、电源zapplication taskprocess优先级stack/ramresponse timeOS serviceBLE taskReset state中32k200msButton task按键状态检测高4k50msLed taskble状态机维护低4kble event callback一般由原厂提供ble状态机维护低workspace taskpower mange task4kdebug port / logging task4kwatch dog4ksystem IRQgpio按键检测4kdriverflashstore key有了这些需求后产品才算完善起来其他需求不在本文讨论之列暂时不涉及大概需求评估到位了需要选项前期选型主要关心的实现需求会遇到的瓶颈问题例如屏幕这种选项与项目经验有关AI时代可以借助AI弥补一下。需求选型瓶颈60fps oled12864 显示屏ram、cpu128M下面进入原型的设计阶段这时就是实际代码的编写了原有逻辑可拆分为这两个按键5S蓝牙开可发现广播新平板可连接60s超时回连旧设备https://www.doubao.com/thread/we80b6591467f8eea(AI 生成的代码框架)typedef enum { BLE_STANDBY, // 待机 BLE_INIT, // 初始化 BLE_ADVERTISING,// 广播 BLE_CONNECTED // 已连接 } ble_state_t; static ble_state_t s_ble_state BLE_STANDBY; // 外部任何地方都能调用获取当前 BLE 状态 ble_state_t ble_get_state(void) { return s_ble_state; } void ble_task(void *arg) { // 一开始 Standby s_ble_state BLE_STANDBY; for (;;) { // 阻塞等待事件Task Notify 核心 ulTaskNotifyTake(pdTRUE, portMAX_DELAY); switch (s_ble_state) { case BLE_STANDBY: // 收到启动通知 → 进 Init ble_init(); s_ble_state BLE_INIT; break; case BLE_INIT: // Init 完成 → 开广播 ble_start_advertising(); s_ble_state BLE_ADVERTISING; break; case BLE_ADVERTISING: // 收到连接事件 → 进连接态 s_ble_state BLE_CONNECTED; on_connected(); break; case BLE_CONNECTED: // 断开 → 回 Standby 或重启广播 if (disconnect_event) { s_ble_state BLE_STANDBY; } break; } } } #include FreeRTOS.h #include task.h // -------------------------- // 按键事件类型对应流程图 // -------------------------- typedef enum { BTN_EVENT_NONE, BTN_SINGLE_PRESS, BTN_DOUBLE_PRESS, BTN_LONG_PRESS } btn_event_t; // -------------------------- // 任务句柄 // -------------------------- static TaskHandle_t btn_task_handle NULL; static TaskHandle_t ble_task_handle NULL; // 蓝牙任务句柄你已有的 // -------------------------- // 外部获取按键状态接口可选 // -------------------------- btn_event_t btn_get_last_event(void); // -------------------------- // 硬件按键检测简化版你自己实现 // -------------------------- static bool btn_is_pressed(void) { // 这里替换成你的 GPIO 读值逻辑 return false; } // -------------------------- // 按键任务主循环完全对应你的流程图 // -------------------------- void button_task(void *arg) { btn_event_t last_event BTN_EVENT_NONE; TickType_t press_start_tick 0; uint8_t press_count 0; for (;;) { // 1. 等待按键中断/定时器通知Task Notify 核心 ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // 2. Button Pressure check进入检测逻辑 if (btn_is_pressed()) { press_start_tick xTaskGetTickCount(); press_count; // 等待按键释放 while (btn_is_pressed()) { vTaskDelay(pdMS_TO_TICKS(10)); } TickType_t press_duration xTaskGetTickCount() - press_start_tick; // 3. 分支判断长按/双击/单击 if (press_duration pdMS_TO_TICKS(1000)) { // 长按阈值1s // long press → Process Reset State last_event BTN_LONG_PRESS; xTaskNotifyGive(user_ble_task_handle); } else { // 检查是否双击在300ms内再次按下 vTaskDelay(pdMS_TO_TICKS(300)); if (btn_is_pressed()) { // double press → Process 1 last_event BTN_DOUBLE_PRESS; Process_1(); // 你的双击处理函数 } else { // single press → Process 2 last_event BTN_SINGLE_PRESS; Process_2(); // 你的单击处理函数 } } } } } // -------------------------- // 按键中断通知任务ISR 版本 // -------------------------- void EXTI_Button_IRQHandler(void) { BaseType_t xHigherPriorityTaskWoken pdFALSE; // 通知按键任务开始检测 vTaskNotifyGiveFromISR(btn_task_handle, xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } // -------------------------- // 任务创建 // -------------------------- void button_task_init(TaskHandle_t ble_task_hdl) { ble_task_handle ble_task_hdl; xTaskCreate( button_task, btn_task, 1024, NULL, 2, btn_task_handle ); } void user_ble_task(void *arg) { for (;;) { // 等待按键长按通知 ulTaskNotifyTake(pdTRUE, portMAX_DELAY); ble_state_t current ble_get_state(); switch (current) { case BLE_STANDBY: // 直接通知开可发现广播 xTaskNotifyGive(ble_task_handle_discoverable_adv); s_ble_state BLE_ADVERTISING; break; case BLE_INIT: // 等待进入Standby while (ble_get_state() ! BLE_STANDBY) { vTaskDelay(pdMS_TO_TICKS(10)); } // 通知开可发现广播 xTaskNotifyGive(ble_task_handle_discoverable_adv); s_ble_state BLE_ADVERTISING; break; case BLE_ADVERTISING: // 先关广播 ble_stop_advertising(); // 通知开可发现广播 xTaskNotifyGive(ble_task_handle_discoverable_adv); break; case BLE_CONNECTED: // 断连 ble_disconnect(); // 等待断开完成 while (ble_get_state() BLE_CONNECTED) { vTaskDelay(pdMS_TO_TICKS(10)); } // 通知开可发现广播 xTaskNotifyGive(ble_task_handle_discoverable_adv); s_ble_state BLE_ADVERTISING; break; } } }流程图在迭代一下就长这样了这个作为测试部门同事使用用来排列组合出testcase测试最终效果最后实际验证发现还是会回连到原有设备上。因为每开一次广播地址不变就会回连所以要使用固定地址IRK的方式区分新旧连接所以需求又被细化到了IRK上正常模式休眠下按键开广播有配对固定地址旧IRK生成RPA开不可发现广播回连旧设备无配对开可发现广播固定地址新IRK生成RPA连接成功储存IRK新设备连接模式按键5S固定地址新IRK生成RPA开可发现广播新平板可连接连接成功储存IRK在情况1后60s超时固定地址旧IRK生成RPA开不可发现广播回连旧设备生产模式处于正常模式有无配对都是固定地址开可发现广播。这个时候就体现了流程图的好处了---流程图能够快速迭代添加功能需求分离设计需求导入实际代码过程这个时候要回到最原始的生产的需求怎么办难不成删掉不用直接在模块函数的开头加宏判断是否跳过即可实现 流程图 回退生产模式处于正常模式有无配对都是固定地址开可发现广播。// 总功能宏 #define FEATURE_NEW_IRK_EN 1 // 1开启 0关闭整个IRK模块 // IRK 内部数据结构 typedef struct { uint8_t irk[16]; // 身份解析密钥 uint8_t addr[6]; // 绑定地址 bool is_paired; // 是否已配对 bool flag; // 运行时功能开关从宏初始化 } IRKData; // 事件回调类型定义 typedef void (*irk_callback_t)(void); // 面向对象封装Data Event Feature Flag typedef struct { // 数据区 IRKData data; // 事件/方法区你要的三个函数 void (*rand_new_irk)(void); void (*use_stored_irk)(void); void (*rand_replace_stored_irk)(void); } IRK_reset_feature; // 全局唯一IRK功能对象 IRK_reset_feature irk_feature { // 数据初始化flag 从宏赋值 .data { .flag FEATURE_NEW_IRK_EN, }, // 方法绑定 .rand_new_irk irk_rand_new_irk, .use_stored_irk irk_use_stored_irk, .rand_replace_stored_irk irk_rand_replace_stored_irk, }; // --------------------------------------- // 1. 生成新IRK // --------------------------------------- static void irk_rand_new_irk(void) { // 入口 feature 判断 if (!FEATURE_NEW_IRK_EN || !irk_feature.data.flag) { return; } // 你的逻辑生成随机IRK // ... } // --------------------------------------- // 2. 使用存储的IRK // --------------------------------------- static void irk_use_stored_irk(void) { // 入口 feature 判断 if (!FEATURE_NEW_IRK_EN || !irk_feature.data.flag) { return; } // 你的逻辑使用Flash存储的IRK // ... } // --------------------------------------- // 3. 随机替换存储IRK // --------------------------------------- static void irk_rand_replace_stored_irk(void) { // 入口 feature 判断 if (!FEATURE_NEW_IRK_EN || !irk_feature.data.flag) { return; } // 你的逻辑随机新IRK并覆盖存储 // ... } // 外部调用示例 irk_feature.rand_new_irk(); irk_feature.use_stored_irk(); irk_feature.rand_replace_stored_irk();feature函数new IRKrand_new_irkuse_stored_irkrand_relapce_stored_irkpress start discover adv。。。这种表格就可以交给项目去评估具体的项目开发难度并且排期这样就是需求的快速导入合规所有的开发流程产品项目方可以只对流程图的细节coding只需要实现feature 模块子函数实现高效的需求、流程、编码隔离后期debug也方便一般公司会维护多个产品的代码新的需求导入客户需要快速体验有和没有的差距这一部分coding是没办法自己替代的但是出了一版又一版不仅麻烦自己也容易糊涂其他人review起来也困难。使用逻辑化的语言能够使得大家对代码状态有清晰的认知不会说责任都在coding身上