diff --git a/AC701N.cbp b/AC701N.cbp index 32008f5..f2b466b 100644 --- a/AC701N.cbp +++ b/AC701N.cbp @@ -176,6 +176,7 @@ + @@ -778,6 +779,10 @@ + + + diff --git a/Makefile b/Makefile index 88b7db4..87fb460 100644 --- a/Makefile +++ b/Makefile @@ -250,6 +250,7 @@ INCLUDES := \ -Iapps/usr_le_code/thirdpart/mjson \ -Iapps/usr_jb_proto/Protocol \ -Iapps/usr_jb_proto/Utils \ + -Iapps/usr_periph \ -I$(SYS_INC_DIR) \ @@ -619,6 +620,7 @@ c_SRC_FILES := \ apps/usr_jb_proto/Utils/jb_common.c \ apps/usr_jb_proto/Utils/jb_ringbuffer.c \ apps/usr_jb_proto/usr_jb_main.c \ + apps/usr_periph/usr_rtc.c \ # usr_le_code 静态库(协议编解码 + 广播/收发) diff --git a/apps/common/device/key/iokey.c b/apps/common/device/key/iokey.c index 03ee5e0..95d6b13 100644 --- a/apps/common/device/key/iokey.c +++ b/apps/common/device/key/iokey.c @@ -157,7 +157,77 @@ __attribute__((weak)) u8 iokey_filter_hook(u8 io_state) { return 0; } -u8 usr_first_power=0; + +/* 开机锁键剩余确认次数;非 0 时扫描一律返回 NO_KEY */ +static u8 s_boot_lock_cnt; + +/** + * @brief 启动/关闭开机锁键 + * @param release_cnt >0:松开后连续确认次数;0:关闭 + */ +void iokey_boot_lock_start(u8 release_cnt) +{ +#if TCFG_IOKEY_BOOT_LOCK_ENABLE + s_boot_lock_cnt = release_cnt; +#else + (void)release_cnt; + s_boot_lock_cnt = 0; +#endif +} + +/** + * @brief 立即关闭开机锁键 + */ +void iokey_boot_lock_stop(void) +{ + s_boot_lock_cnt = 0; +} + +/** + * @brief 扫描路径更新锁键:监视 port[0],松开则递减计数 + * @return 1=仍锁定(应返回 NO_KEY),0=已解锁可正常扫描 + */ +static u8 iokey_boot_lock_update(void) +{ +#if !TCFG_IOKEY_BOOT_LOCK_ENABLE + return 0; +#else + u8 key_io; + u8 io_level; + u8 released; + + if (!s_boot_lock_cnt) + { + return 0; + } + if (!__this || (__this->num == 0)) + { + return 1; + } + + key_io = __this->port[0].key_type.one_io.port; + io_level = gpio_read(key_io); + if (__this->port[0].connect_way == ONE_PORT_TO_LOW) + { + released = io_level; /* 上拉输入,松开为高 */ + } + else + { + released = !io_level; /* 下拉输入,松开为低 */ + } + + if (released) + { + s_boot_lock_cnt--; + if (s_boot_lock_cnt == 0) + { + puts("iokey boot lock released\n"); + } + } + return 1; /* 本拍仍屏蔽;计数到 0 后下一拍才开放 */ +#endif +} + u8 io_get_key_value(void) { int i; @@ -173,12 +243,7 @@ u8 io_get_key_value(void) if (!__this->enable) { return NO_KEY; } - if(usr_first_power) - { - if(gpio_read(IO_PORTB_01)) - { - usr_first_power--; - } + if (iokey_boot_lock_update()) { return NO_KEY; } //先扫描单IO接按键方式 @@ -266,7 +331,7 @@ _iokey_get_value_end: int iokey_init(const struct iokey_platform_data *iokey_data) { int i; - usr_first_power=10; + __this = iokey_data; if (__this == NULL) { return -EINVAL; @@ -275,6 +340,12 @@ int iokey_init(const struct iokey_platform_data *iokey_data) return KEY_NOT_SUPPORT; } + /* 长按开机场景:等电源键(port[0])松开后再开放按键扫描 */ +#if TCFG_IOKEY_BOOT_LOCK_ENABLE + iokey_boot_lock_start(IOKEY_BOOT_LOCK_DEFAULT_CNT); +#else + iokey_boot_lock_stop(); +#endif key_io_reset(); return 0; diff --git a/apps/common/third_party_profile/jieli/JL_rcsp/bt_trans_data/le_rcsp_adv_module.c b/apps/common/third_party_profile/jieli/JL_rcsp/bt_trans_data/le_rcsp_adv_module.c index 5354771..0edd9c6 100644 --- a/apps/common/third_party_profile/jieli/JL_rcsp/bt_trans_data/le_rcsp_adv_module.c +++ b/apps/common/third_party_profile/jieli/JL_rcsp/bt_trans_data/le_rcsp_adv_module.c @@ -57,6 +57,7 @@ #include "chgbox_box.h" #endif +/* usr_le_code 库钩子:由静态库导出,供 RCSP BLE 栈回调,非应用层公开 API */ extern void usr_le_data_recieve(u8* data,u16 len); extern void usr_ble_adv_init(); #if 1 @@ -526,19 +527,15 @@ static int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_h void usr_ble_send_data(u8* data,u16 len) { + /* CCC 未写时 ATT 发数会失败(常见 app_send_fail:-93),等主机使能通知再发 */ + if (get_ble_work_state() != BLE_ST_NOTIFY_IDICATE) { + log_info("ble send skip, wait CCC"); + return; + } if (app_send_user_data_check(len)) { app_send_user_data(ATT_CHARACTERISTIC_15f1e601_a277_43fc_a484_dd39ef8a9100_01_VALUE_HANDLE, data, len, ATT_OP_AUTO_READ_CCC); } - //printf("send rsp:"); - // printf_buf(data,len); - printf("\n"); - for(u8 i=0;ibytes_data = (pkt->device_bytes_len > 0) ? p : NULL; - - printf("cmd = %d", pkt->cmd); - printf("reserved = %d", pkt->reserved); - printf("gateway_int_cnt = %d", pkt->gateway_int_cnt); - printf("device_int_len = %d", pkt->device_int_len); - printf("device_str_len = %d", pkt->device_str_len); - printf("device_bytes_len = %d", pkt->device_bytes_len); + // printf("cmd = %d", pkt->cmd); + // printf("reserved = %d", pkt->reserved); + // printf("gateway_int_cnt = %d", pkt->gateway_int_cnt); + // printf("device_int_len = %d", pkt->device_int_len); + // printf("device_str_len = %d", pkt->device_str_len); + // printf("device_bytes_len = %d", pkt->device_bytes_len); - /* int_data 指向 buf 内部,地址未必 4 字节对齐, - * 不能直接解引用 int32_t*(对齐访问会触发硬件异常); - * 必须按字节读取,用 get_be32 还原数值 */ - if (pkt->int_data && pkt->device_int_len >= 4) { - const uint8_t *ip = (const uint8_t *)pkt->int_data; - printf("int_data = "); - for (uint16_t i = 0; i < pkt->device_int_len / 4u; i++) { - printf("%08X ", (unsigned int)get_be32(ip + (uint32_t)i * 4u)); - } - printf("\n"); - } - /* str_data / bytes_data 不保证有 '\0',用 printf_buf 打印 hex */ - if (pkt->str_data && pkt->device_str_len > 0) { - printf("str_data = "); - for (size_t i = 0; i < pkt->device_str_len; i++) { - printf("%02X ", *(pkt->str_data + i)); - } - printf("\n"); - } - if (pkt->bytes_data && pkt->device_bytes_len > 0) { - printf("bytes_data = "); - for (size_t i = 0; i < pkt->device_bytes_len; i++) { - printf("%02X ", *(pkt->bytes_data + i)); - } - printf("\n"); - } - printf("crc = %04X", crc_recv); - printf("\n"); + // /* int_data 指向 buf 内部,地址未必 4 字节对齐, + // * 不能直接解引用 int32_t*(对齐访问会触发硬件异常); + // * 必须按字节读取,用 get_be32 还原数值 */ + // if (pkt->int_data && pkt->device_int_len >= 4) { + // const uint8_t *ip = (const uint8_t *)pkt->int_data; + // printf("int_data = "); + // for (uint16_t i = 0; i < pkt->device_int_len / 4u; i++) { + // printf("%08X ", (unsigned int)get_be32(ip + (uint32_t)i * 4u)); + // } + // printf("\n"); + // } + // /* str_data / bytes_data 不保证有 '\0',用 printf_buf 打印 hex */ + // if (pkt->str_data && pkt->device_str_len > 0) { + // printf("str_data = "); + // for (size_t i = 0; i < pkt->device_str_len; i++) { + // printf("%02X ", *(pkt->str_data + i)); + // } + // printf("\n"); + // } + // if (pkt->bytes_data && pkt->device_bytes_len > 0) { + // printf("bytes_data = "); + // for (size_t i = 0; i < pkt->device_bytes_len; i++) { + // printf("%02X ", *(pkt->bytes_data + i)); + // } + // printf("\n"); + // } + // printf("crc = %04X", crc_recv); + // printf("\n"); return BLE_PROTO_OK; } diff --git a/apps/usr_le_code/bleproto.c b/apps/usr_le_code/bleproto.c index b53474d..89992ad 100644 --- a/apps/usr_le_code/bleproto.c +++ b/apps/usr_le_code/bleproto.c @@ -222,106 +222,113 @@ int bleproto_appdata_encode(uint8_t *txbuf, int rc; uint16_t txbuf_size = size; - bleproto_appdata_t appdata; + /* + * appdata.data[] 在 SMALL 模式下约 2.3KB,供 JSON/OTA 编码中间缓冲。 + * JB 55AA 透传可走 bleproto_appdata_wrap_raw,不必经本函数。 + * 此处用 static,避免在调用方任务栈上临时分配。 + */ + static bleproto_appdata_t s_enc_appdata; + bleproto_appdata_t *appdata = &s_enc_appdata; + /* header */ { - appdata.header = desc->header; + appdata->header = desc->header; } /* data */ - if (appdata.header.msgtype == BLEPROTO_APPDATA_MSGTYPE_REQ) + if (appdata->header.msgtype == BLEPROTO_APPDATA_MSGTYPE_REQ) { - switch (appdata.header.serviceid) + switch (appdata->header.serviceid) { case E_BLEPROTO_SERVICE_ID_DEVICEINFO: rc = bleproto_encjson_deviceinfo_req(&desc->datast.deviceinfo_req, - appdata.data); + appdata->data); break; case E_BLEPROTO_SERVICE_ID_AUTHSETUP: rc = bleproto_encjson_authsetup_req(&desc->datast.authsetup_req, - appdata.data); + appdata->data); break; case E_BLEPROTO_SERVICE_ID_AUTHDELETE: rc = bleproto_encjson_authdelete_req(&desc->datast.authdelete_req, - appdata.data); + appdata->data); break; case E_BLEPROTO_SERVICE_ID_RUNCMD: rc = bleproto_encjson_runcmd_req(&desc->datast.runcmd_req, - appdata.data); + appdata->data); break; case E_BLEPROTO_SERVICE_ID_REPORTCMD: rc = bleproto_encjson_reportcmd_req(&desc->datast.reportcmd_req, - appdata.data); + appdata->data); break; case E_BLEPROTO_SERVICE_ID_DOACTION: rc = bleproto_encjson_doaction_req(&desc->datast.doaction_req, - appdata.data); + appdata->data); break; case E_BLEPROTO_SERVICE_ID_OTANOTIFY: rc = bleproto_encjson_otanotify_req(&desc->datast.otanotify_req, - appdata.data); + appdata->data); break; case E_BLEPROTO_SERVICE_ID_OTADATA: rc = bleproto_encjson_otadata_req(&desc->datast.otadata_req, - appdata.data); + appdata->data); break; case E_BLEPROTO_SERVICE_ID_RUNCMD_V2: - rc = ble_proto_pack(&desc->datast.runcmd_v2_req, appdata.data, sizeof(appdata.data)); + rc = ble_proto_pack(&desc->datast.runcmd_v2_req, appdata->data, sizeof(appdata->data)); break; case E_BLEPROTO_SERVICE_ID_REPORTCMD_V2: - rc = ble_proto_pack(&desc->datast.reportcmd_v2_req, appdata.data, sizeof(appdata.data)); + rc = ble_proto_pack(&desc->datast.reportcmd_v2_req, appdata->data, sizeof(appdata->data)); break; case E_BLEPROTO_SERVICE_ID_OTADATA_V2: - rc = ble_proto_pack(&desc->datast.otadata_v2_req, appdata.data, sizeof(appdata.data)); + rc = ble_proto_pack(&desc->datast.otadata_v2_req, appdata->data, sizeof(appdata->data)); break; default: /* unkown serviceid */ return (-1); } } - else if (appdata.header.msgtype == BLEPROTO_APPDATA_MSGTYPE_RSP) + else if (appdata->header.msgtype == BLEPROTO_APPDATA_MSGTYPE_RSP) { - switch (appdata.header.serviceid) + switch (appdata->header.serviceid) { case E_BLEPROTO_SERVICE_ID_DEVICEINFO: rc = bleproto_encjson_deviceinfo_rsp(&desc->datast.deviceinfo_rsp, - appdata.data); + appdata->data); break; case E_BLEPROTO_SERVICE_ID_AUTHSETUP: rc = bleproto_encjson_authsetup_rsp(&desc->datast.authsetup_rsp, - appdata.data); + appdata->data); break; case E_BLEPROTO_SERVICE_ID_AUTHDELETE: rc = bleproto_encjson_authdelete_rsp(&desc->datast.authdelete_rsp, - appdata.data); + appdata->data); break; case E_BLEPROTO_SERVICE_ID_RUNCMD: rc = bleproto_encjson_runcmd_rsp(&desc->datast.runcmd_rsp, - appdata.data); + appdata->data); break; case E_BLEPROTO_SERVICE_ID_REPORTCMD: rc = bleproto_encjson_reportcmd_rsp(&desc->datast.reportcmd_rsp, - appdata.data); + appdata->data); break; case E_BLEPROTO_SERVICE_ID_DOACTION: rc = bleproto_encjson_doaction_rsp(&desc->datast.doaction_rsp, - appdata.data); + appdata->data); break; case E_BLEPROTO_SERVICE_ID_OTANOTIFY: rc = bleproto_encjson_otanotify_rsp(&desc->datast.otanotify_rsp, - appdata.data); + appdata->data); break; case E_BLEPROTO_SERVICE_ID_OTADATA: rc = bleproto_encbin_otadata_rsp(&desc->datast.otadata_rsp, - appdata.data); + appdata->data); break; case E_BLEPROTO_SERVICE_ID_RUNCMD_V2: - rc = ble_proto_pack(&desc->datast.runcmd_v2_rsp, appdata.data, sizeof(appdata.data)); + rc = ble_proto_pack(&desc->datast.runcmd_v2_rsp, appdata->data, sizeof(appdata->data)); break; case E_BLEPROTO_SERVICE_ID_REPORTCMD_V2: - rc = ble_proto_pack(&desc->datast.reportcmd_v2_rsp, appdata.data, sizeof(appdata.data)); + rc = ble_proto_pack(&desc->datast.reportcmd_v2_rsp, appdata->data, sizeof(appdata->data)); break; case E_BLEPROTO_SERVICE_ID_OTADATA_V2: - rc = ble_proto_pack(&desc->datast.otadata_v2_rsp, appdata.data, sizeof(appdata.data)); + rc = ble_proto_pack(&desc->datast.otadata_v2_rsp, appdata->data, sizeof(appdata->data)); break; default: /* unkown serviceid */ @@ -340,10 +347,10 @@ int bleproto_appdata_encode(uint8_t *txbuf, } /* datalen */ - appdata.header.datalen = (uint16_t)rc; + appdata->header.datalen = (uint16_t)rc; /* appdata_serialize */ - return bleproto_appdata_serialize(&appdata, packetlen, txbuf, txbuf_size); + return bleproto_appdata_serialize(appdata, packetlen, txbuf, txbuf_size); } int bleproto_appdata_decode(uint8_t *rxbuf, diff --git a/apps/usr_le_code/bleproto_packer.c b/apps/usr_le_code/bleproto_packer.c index c82c16b..b1c6159 100644 --- a/apps/usr_le_code/bleproto_packer.c +++ b/apps/usr_le_code/bleproto_packer.c @@ -671,6 +671,55 @@ int bleproto_appdata_serialize(bleproto_appdata_t *appdata, return (offset); } +int bleproto_appdata_wrap_raw(uint8_t *txbuf, + uint16_t txbuf_size, + const bleproto_appdata_header_t *header, + const uint8_t *payload, + uint16_t payload_len) +{ + uint16_t total; + + if (!txbuf || !header) { + BLEPROTO_T_E("Invalid parameters"); + return -1; + } + if (payload_len > 0 && !payload) { + BLEPROTO_T_E("Invalid payload"); + return -1; + } + /* 单帧 this_frame_len 为 uint8,超过 255 需走分包 serialize */ + if (payload_len > 255u) { + BLEPROTO_T_E("payload too long for single frame"); + return -1; + } + + total = (uint16_t)(BLEPROTO_APPDATA_HEADR_LEN + payload_len); + if (txbuf_size < total) { + BLEPROTO_T_E("txbuf too short"); + return -1; + } + + /* buf[0]: version[2:0] | datafmt[3:3] | msgtype[7:4] */ + txbuf[0] = UTILS_BITFIELD_SET(header->version, 2, 0) | + UTILS_BITFIELD_SET(header->datafmt, 3, 3) | + UTILS_BITFIELD_SET(header->msgtype, 7, 4); + /* buf[1]: msgid[3:0] | encrypt[4:4] | reserve[7:5] */ + txbuf[1] = UTILS_BITFIELD_SET(header->msgid, 3, 0) | + UTILS_BITFIELD_SET(header->encrypt, 4, 4) | + UTILS_BITFIELD_SET(header->reserve, 7, 5); + txbuf[2] = 1; /* totalframe */ + txbuf[3] = 0; /* frameseq:单帧为 0 */ + txbuf[4] = (uint8_t)payload_len; /* this frame payload len */ + txbuf[5] = header->serviceid; + proto_bytes_put_le16(&txbuf[6], payload_len); + + if (payload_len > 0) { + memcpy(&txbuf[BLEPROTO_APPDATA_HEADR_LEN], payload, payload_len); + } + + return (int)total; +} + int bleproto_appdata_deserialize(uint8_t * buf, uint16_t len, bleproto_appdata_t *appdata) @@ -1958,8 +2007,12 @@ int bleproto_encbin_otadata_rsp(const bleproto_otadata_rsp_t *p, data[offset++] = (uint8_t)((p->len >> 16) & 0xFF); data[offset++] = (uint8_t)((p->len >> 24) & 0xFF); - /* serialize data */ + /* serialize data(data 为外部指针,不再内嵌数组) */ if (p->len > 0 && p->len <= BLEPROTO_APPDATA_OTADATA_SZ) { + if (!p->data) { + BLEPROTO_T_E("otadata_rsp.data is NULL"); + return -1; + } memcpy(&data[offset], p->data, p->len); offset += p->len; } @@ -2984,10 +3037,12 @@ int bleproto_debin_otadata_rsp(const uint8_t * data, return -1; } - /* deserialize data */ + /* 零拷贝:指向输入缓冲,调用方须在下次 decode 前用完 */ if (rsp->len > 0) { - memcpy(rsp->data, &data[offset], rsp->len); + rsp->data = &data[offset]; offset += rsp->len; + } else { + rsp->data = NULL; } return offset; diff --git a/apps/usr_le_code/bleproto_packer.h b/apps/usr_le_code/bleproto_packer.h index 9b5661f..8ec82c1 100644 --- a/apps/usr_le_code/bleproto_packer.h +++ b/apps/usr_le_code/bleproto_packer.h @@ -483,7 +483,12 @@ typedef struct bleproto_otadata_rsp { int id; ///< file_id int32_t offset; ///< 当前读取到的数据偏移 uint32_t len; ///< 当次读取数据的大小 - uint8_t data[BLEPROTO_APPDATA_OTADATA_SZ]; ///< 读取的数据 + /** + * 固件数据指针(不内嵌大数组,避免 bleproto_appdata_desc_t 膨胀到 2KB+) + * - decode:指向 appdata 内部缓冲(零拷贝),下次 decode 前有效 + * - encode:由调用方提供可读缓冲 + */ + const uint8_t *data; } bleproto_otadata_rsp_t; /****************************************************************************/ /* Public Data */ @@ -580,6 +585,22 @@ int bleproto_appdata_serialize(bleproto_appdata_t *appdata, uint8_t * buf, uint16_t size); +/** + * @brief 将已打包好的 payload 包装成单帧 appdata(不分包) + * @param txbuf 输出缓冲 + * @param txbuf_size 输出缓冲大小 + * @param header 应用层头(datalen/totalframe/frameseq 由本函数填写) + * @param payload 已序列化的 service 数据;可为 NULL(payload_len==0) + * @param payload_len payload 长度,须 <=255(单帧 pthis_frame_len 为 u8) + * @return >0 总字节数;<0 失败 + * @note 小包场景(JB 55AA 透传等)用此接口,避免 bleproto_appdata_t 大缓冲 + */ +int bleproto_appdata_wrap_raw(uint8_t *txbuf, + uint16_t txbuf_size, + const bleproto_appdata_header_t *header, + const uint8_t *payload, + uint16_t payload_len); + /** * @brief 反序列化应用数据 * diff --git a/apps/usr_le_code/lib/libusr_le_code.a b/apps/usr_le_code/lib/libusr_le_code.a index 5509907..f337979 100644 Binary files a/apps/usr_le_code/lib/libusr_le_code.a and b/apps/usr_le_code/lib/libusr_le_code.a differ diff --git a/apps/usr_le_code/usr_le_adv.c b/apps/usr_le_code/usr_le_adv.c index befd16e..9faa88c 100644 --- a/apps/usr_le_code/usr_le_adv.c +++ b/apps/usr_le_code/usr_le_adv.c @@ -1,102 +1,163 @@ +/****************************************************************************** + * @file usr_le_adv.c + * @brief BLE 广播 / 配对 / OTA 广播切换 + * @author cyWu <1917507415@qq.com> + * @date 2026.08.19 + * @version V1.1.0 + * @history + * - V1.0.0, 2026.08.03, cyWu, 首次发布 + * - V1.1.0, 2026.08.19, cyWu, 对齐模组注释与助手;保留 SOC 上电绑广播策略 + ******************************************************************************/ + #include "system/includes.h" #include "app_config.h" -#include "asm/pwm_led.h" -#include "tone_player.h" -#include "ui_manage.h" #include "app_main.h" -#include "app_task.h" -#include "asm/charge.h" -#include "app_power_manage.h" -#include "app_charge.h" -#include "user_cfg.h" - -#include "audio.h" #include "vm.h" #include "bleproto.h" #include "bleproto_api.h" #include "usr_le_product.h" -#define LOG_TAG_CONST APP -#define LOG_TAG "[APP]" + +#define LOG_TAG_CONST APP +#define LOG_TAG "[LE_ADV]" #define LOG_ERROR_ENABLE #define LOG_DEBUG_ENABLE #define LOG_INFO_ENABLE -/* #define LOG_DUMP_ENABLE */ -#define LOG_CLI_ENABLE #include "debug.h" -extern u8 muti_adv_data[31];//={0}; +/****************************************************************************** + * 广播相关三个标志(务必区分): + * + * 1) usr_auth_data.paired_flag —— 配网结果(存 VM,掉电保持) + * - 0:未配网 + * - 1:已配网(三元组已保存) + * + * 2) usr_var.usr_goto_pair —— 是否主动进入配网流程(RAM,掉电丢失) + * - 0:非配网流程 + * - 1:app_main 上电 / usr_goto_pair_mode() 触发,发【绑定广播】 + * + * 3) usr_var.usr_adv_flag —— 广播内容是否需要刷新到协议栈(一次性) + * - 1:usr_ble_adv_updata() 会重新 set adv data 并 enable + * - 0:本周期不刷新 + * + * SOC 决策表(与模组不同:未配网也发绑定广播,便于上电配网): + * usr_goto_pair==1 → 绑定广播 + * paired_flag==0 → 绑定广播 + * paired_flag==1 → 回连广播 + ******************************************************************************/ + +extern u8 muti_adv_data[31]; extern u8 muti_adv_data_len; /* 定义在 usr_le_recieve.c,库内共用,不对外导出 */ extern bleproto_authsetup_req_t usr_auth_data; -extern u8 usr_get_full(); -extern u16 usr_get_conn_service(); -extern int muti_make_set_adv_data(u8* data,u8 data_len); -extern u8 usr_get_ldoin_all(); +extern u16 usr_get_conn_service(void); +extern int muti_make_set_adv_data(u8 *data, u8 data_len); +extern int muti_make_set_adv_data_updata(void); +extern void usr_set_adv_interval(u16 val); +extern u16 usr_get_adv_interval(void); +extern void ble_multi_trans_disconnect(void); +extern void ble_trans_module_enable(u8 flag); +extern void usr_timer_loop(void); +extern u8 usr_get_ota_status(void); +extern void usr_ota_exit(void); -void usr_ble_adv_updata(); +void usr_ble_adv_init(void); +void usr_ble_adv_updata(void); -static int le_timer_handle=0; -static int le_timer_handle2=0; +static int le_timer_handle = 0; - -extern u8 get_ldo5v_online_hw(void); -extern u8 usr_get_work_status(); -extern void usr_printf_sta(); -extern int muti_make_set_adv_data_updata(); - -void usr_get_pair_info() +/** + * @brief BLE 是否已连接 + * @return 1=已连接,0=未连接 + */ +uint8_t usr_ble_is_connected(void) { - int ret = 0xffff; + return (usr_get_conn_service() != 0) ? 1u : 0u; +} + +/** + * @brief 从 VM 读取配对信息到 usr_auth_data + * @note 失败则清零,并将 paired_flag 置 0 写回 + */ +void usr_get_pair_info(void) +{ + int ret; + ret = syscfg_read(CFG_USER_PAIR_INFO, (u8 *)&usr_auth_data, sizeof(usr_auth_data)); - if(ret<=0) + if (ret <= 0) { - memset(&usr_auth_data,0x00,sizeof(usr_auth_data)); - usr_auth_data.paired_flag=0; + memset(&usr_auth_data, 0x00, sizeof(usr_auth_data)); + usr_auth_data.paired_flag = 0; syscfg_write(CFG_USER_PAIR_INFO, (u8 *)&usr_auth_data, sizeof(usr_auth_data)); } } -void usr_write_pair_info() + +/** + * @brief 标记已配网并保存三元组到 VM + * @note AUTHSETUP 成功后调用,paired_flag = 1 + */ +void usr_write_pair_info(void) { - usr_auth_data.paired_flag=1; + usr_auth_data.paired_flag = 1; syscfg_write(CFG_USER_PAIR_INFO, (u8 *)&usr_auth_data, sizeof(usr_auth_data)); } -void usr_clear_pair_info() +/** + * @brief 清除配对信息并延时复位 + */ +void usr_clear_pair_info(void) { - printf("usr_clear_pair_info\n"); - memset(&usr_auth_data,0x00,sizeof(usr_auth_data)); - usr_auth_data.paired_flag=0; + printf("usr_clear_pair_info_reset\n"); + memset(&usr_auth_data, 0x00, sizeof(usr_auth_data)); + usr_auth_data.paired_flag = 0; syscfg_write(CFG_USER_PAIR_INFO, (u8 *)&usr_auth_data, sizeof(usr_auth_data)); - sys_timeout_add(NULL,cpu_reset,600); + sys_timeout_add(NULL, cpu_reset, 600); } -void usr_clear_pair_info_noreset() + +/** + * @brief 清除配对信息但不复位(用于重新配网) + */ +void usr_clear_pair_info_noreset(void) { - printf("usr_clear_pair_info2\n"); - memset(&usr_auth_data,0x00,sizeof(usr_auth_data)); - usr_auth_data.paired_flag=0; + printf("usr_clear_pair_info_noreset\n"); + memset(&usr_auth_data, 0x00, sizeof(usr_auth_data)); + usr_auth_data.paired_flag = 0; syscfg_write(CFG_USER_PAIR_INFO, (u8 *)&usr_auth_data, sizeof(usr_auth_data)); } -extern void usr_set_adv_interval(u16 val); -extern u16 usr_get_adv_interval(); -extern void ble_multi_trans_disconnect(void); -void usr_ble_adv_init(); -void usr_goto_pair_mode() +/** + * @brief 进入配网模式:清配对 + 置 usr_goto_pair + 发绑定广播 + */ +void usr_goto_pair_mode(void) { ble_multi_trans_disconnect(); usr_clear_pair_info_noreset(); - usr_var.usr_goto_pair=1; + usr_var.usr_goto_pair = 1; os_time_dly(5); usr_ble_adv_init(); } - -void usr_ble_adv_init() +/** + * @brief 断开 BLE 并停止广播(复位/重启前) + */ +void usr_ble_disconnect_and_stop_adv(void) { - int8_t txpower = 0; - uint8_t manucode[BLEPROTO_ADV_TLV_LENGTH_MANUCODE] = { 0 }; + printf("usr_ble_disconnect_and_stop_adv\n"); + ble_multi_trans_disconnect(); + usr_var.usr_goto_pair = 0; + usr_var.usr_adv_flag = 0; + ble_trans_module_enable(0); +} + +/** + * @brief 按当前标志刷新广播策略并启动周期更新 + * @note 见文件头「三个标志」;SOC 未配网也发绑定广播 + */ +void usr_ble_adv_init(void) +{ + int8_t txpower = 0; + uint8_t manucode[BLEPROTO_ADV_TLV_LENGTH_MANUCODE] = {0}; uint8_t prodcode[BLEPROTO_ADV_TLV_LENGTH_PRODCODE]; uint8_t gmac[6] = {0}; uint8_t pcode[BLE_PROTO_ADV_LENGTH_PCODE]; @@ -106,106 +167,111 @@ void usr_ble_adv_init() memcpy(pcode, g_usr_le_pcode, BLE_PROTO_ADV_LENGTH_PCODE); usr_set_adv_interval(80); - if(usr_var.usr_ota_succ_flag)return; - //muti_adv_data - if(usr_var.usr_goto_pair) + if (usr_var.usr_ota_succ_flag) + { + return; + } + + if (usr_var.usr_goto_pair) { /* app_main 上电已置 usr_goto_pair=1 并清配对信息,此处发绑定广播供网关配网 */ usr_clear_pair_info_noreset(); - printf("power on force to par flag= %d\n",usr_auth_data.paired_flag); - { - muti_adv_data_len=bleproto_advdata_encode4bind(muti_adv_data,txpower,manucode,pcode,prodcode); - } - usr_var.usr_adv_flag=1; + printf("goto pair bind adv, paired_flag=%d\n", usr_auth_data.paired_flag); + muti_adv_data_len = bleproto_advdata_encode4bind(muti_adv_data, txpower, + manucode, pcode, prodcode); + usr_var.usr_adv_flag = 1; } else { usr_get_pair_info(); - if(usr_auth_data.paired_flag==0) + if (usr_auth_data.paired_flag == 0) { - muti_adv_data_len=bleproto_advdata_encode4bind(muti_adv_data,txpower,manucode,pcode,prodcode); - usr_var.usr_adv_flag=0; + /* SOC:未配网也发绑定广播(模组此处为不广播) */ + muti_adv_data_len = bleproto_advdata_encode4bind(muti_adv_data, txpower, + manucode, pcode, prodcode); + usr_var.usr_adv_flag = 0; } else { - memcpy(gmac,usr_auth_data.gwmac,6); - muti_adv_data_len=bleproto_advdata_encode4reconn(muti_adv_data,txpower,gmac,manucode,pcode,prodcode); - usr_var.usr_adv_flag=1; + memcpy(gmac, usr_auth_data.gwmac, 6); + muti_adv_data_len = bleproto_advdata_encode4reconn(muti_adv_data, txpower, gmac, + manucode, pcode, prodcode); + usr_var.usr_adv_flag = 1; } } usr_ble_adv_updata(); - if(le_timer_handle==0)le_timer_handle = sys_timer_add(NULL, usr_ble_adv_updata, 500); + if (le_timer_handle == 0) + { + le_timer_handle = sys_timer_add(NULL, usr_ble_adv_updata, 500); + } } -u8 usr_get_pair_flag() +/** + * @brief 获取是否已配网 + * @return 1=已配网,0=未配网 + */ +u8 usr_get_pair_flag(void) { return usr_auth_data.paired_flag; } -/* -void usr_reconn_adv() + +/** + * @brief 周期处理广播刷新(由 500ms 定时器调用) + * @note 仅当 usr_adv_flag==1 时:停广播 → 写入 muti_adv_data → 再开广播 + */ +void usr_ble_adv_updata(void) { - int8_t txpower = 0; - uint8_t gmac[6]={0}; - uint8_t manucode[BLEPROTO_ADV_TLV_LENGTH_MANUCODE] = { 0 }; - uint8_t prodcode[BLEPROTO_ADV_TLV_LENGTH_PRODCODE] = {'C', 'B', 'F', 'D'}; - uint8_t pcode[6]={'P','C','B','F','D','0'}; - memcpy(gmac,usr_auth_data.gwmac,6); - muti_adv_data_len=bleproto_advdata_encode4reconn(muti_adv_data,txpower,gmac,manucode,pcode,prodcode); - usr_adv_flag=1; -}*/ -extern void ble_trans_module_enable(u8 flag); -extern void usr_timer_loop(); -extern u8 usr_get_ota_status(); -void usr_ble_adv_updata() -{ - //printf("usr_ble_adv_updata\n"); - static u16 usr_goto_slow_adv=0; - if(usr_var.usr_adv_flag) + static u16 usr_goto_slow_adv = 0; + + if (usr_var.usr_adv_flag) { ble_trans_module_enable(0); - usr_var.usr_adv_flag=0; - muti_make_set_adv_data(muti_adv_data,muti_adv_data_len); + usr_var.usr_adv_flag = 0; + muti_make_set_adv_data(muti_adv_data, muti_adv_data_len); printf("###muti_adv_data = "); - printf_buf(muti_adv_data,muti_adv_data_len); + printf_buf(muti_adv_data, muti_adv_data_len); ble_trans_module_enable(1); } - if(usr_get_conn_service()==0) + if (!usr_ble_is_connected()) { - if(usr_get_adv_interval()==80) + /* 未连接:回连快广播(80)持续约 60s(500ms×120)后切慢广播 */ + if (usr_get_adv_interval() == 80) { - if((usr_var.usr_goto_pair==0)&&(usr_auth_data.paired_flag)) + if ((usr_var.usr_goto_pair == 0) && usr_auth_data.paired_flag) { - if(usr_var.usr_ota_succ_flag==0) + if (usr_var.usr_ota_succ_flag == 0) { usr_goto_slow_adv++; - printf("usr_goto_slow_adv = %d\n",usr_goto_slow_adv); + printf("usr_goto_slow_adv = %d\n", usr_goto_slow_adv); } - if(usr_goto_slow_adv>=120) + if (usr_goto_slow_adv >= 120) { - usr_goto_slow_adv=0; - usr_set_adv_interval(80*10); - usr_var.usr_adv_flag=1; + usr_goto_slow_adv = 0; + usr_set_adv_interval(80 * 10); + usr_var.usr_adv_flag = 1; } } } else { - usr_goto_slow_adv=0; + usr_goto_slow_adv = 0; } } else { - usr_goto_slow_adv=0; + usr_goto_slow_adv = 0; } - - if(usr_get_ota_status()==0) + if (usr_get_ota_status() == 0) { - if(usr_get_conn_service()==0) + if (!usr_ble_is_connected()) { - if(usr_var.usr_ota_succ_flag==0)usr_ota_exit(); + if (usr_var.usr_ota_succ_flag == 0) + { + usr_ota_exit(); + } } } @@ -213,36 +279,36 @@ void usr_ble_adv_updata() usr_timer_loop(); } -void usr_soft_off() +void usr_soft_off(void) { sys_enter_soft_poweroff(NULL); } -void usr_ble_adv_updata_OTA() +void usr_ble_adv_updata_OTA(void) { - //printf("usr_ble_adv_updata\n"); ble_trans_module_enable(0); - usr_var.usr_adv_flag=0; + usr_var.usr_adv_flag = 0; extern u8 usr_mac_addr[6]; extern int le_controller_set_mac(void *addr); - usr_mac_addr[0]=usr_mac_addr[0]+1; - le_controller_set_mac(usr_mac_addr); //修改BLE地址 + usr_mac_addr[0] = usr_mac_addr[0] + 1; + le_controller_set_mac(usr_mac_addr); muti_make_set_adv_data_updata(); printf("###muti_adv_data = "); - printf_buf(muti_adv_data,muti_adv_data_len); + printf_buf(muti_adv_data, muti_adv_data_len); ble_trans_module_enable(1); } -void usr_ota_process() +void usr_ota_process(void) { static u8 cnt = 0; + if (cnt == 0) { ble_multi_trans_disconnect(); } - if (usr_get_conn_service() == 0) + if (!usr_ble_is_connected()) { cnt++; if (cnt == 2) @@ -261,7 +327,10 @@ void usr_ota_process() } } -void usr_app_ota_init() +/** + * @brief 旧版 RCSP/JSON OTA 入口;产品 JB 0x30/0x32 路径不要调用 + */ +void usr_app_ota_init(void) { usr_var.usr_goto_updata = 1; if (usr_var.ota_timer == 0) diff --git a/apps/usr_le_code/usr_le_api.h b/apps/usr_le_code/usr_le_api.h index 5e5b20f..4ccb3ba 100644 --- a/apps/usr_le_code/usr_le_api.h +++ b/apps/usr_le_code/usr_le_api.h @@ -1,11 +1,15 @@ /****************************************************************************** * @file usr_le_api.h - * @brief usr_le_code 静态库对外 API(广播 / 收发 / 配网) + * @brief usr_le_code 静态库对外应用 API(配网 / 配对 / OTA) * @author cyWu <1917507415@qq.com> * @date 2026.08.03 - * @version V1.0.0 + * @version V1.0.1 * @history * - V1.0.0, 2026.08.03, cyWu, 首次发布 + * - V1.0.1, 2026.08.19, cyWu, 收敛公开 API,移除 BLE 栈钩子声明 + * + * @note usr_ble_adv_init / usr_le_data_recieve 为 BLE/RCSP 协议栈链接钩子, + * 由库内部实现并导出符号,不作为应用层公开接口。 ******************************************************************************/ #ifndef __USR_LE_API_H__ @@ -17,20 +21,6 @@ extern "C" { #endif -/** - * @brief 初始化 BLE 广播数据(绑定广播或重连广播) - * @note 根据配对状态与 usr_goto_pair 选择 bind/reconn 广播,并启动 500ms 周期更新 - */ -void usr_ble_adv_init(void); - -/** - * @brief BLE 协议栈接收数据入口 - * @param data 接收缓冲区 - * @param len 本次接收长度 - * @note 组包后解析 bleproto,并分发 DEVICEINFO/AUTH/RUNCMD/OTA 等服务 - */ -void usr_le_data_recieve(uint8_t *data, uint16_t len); - /** * @brief 进入配对/配网模式 * @note 断开当前连接、清除配对信息(不复位),重新发绑定广播 diff --git a/apps/usr_le_code/usr_le_recieve.c b/apps/usr_le_code/usr_le_recieve.c index 5a28e16..9c6eaa1 100644 --- a/apps/usr_le_code/usr_le_recieve.c +++ b/apps/usr_le_code/usr_le_recieve.c @@ -1,181 +1,202 @@ +/****************************************************************************** + * @file usr_le_recieve.c + * @brief BLE 收包入口:bleproto 解析后分发配网 / 对时 / JB 协议 / 组应答 + * @author cyWu <1917507415@qq.com> + * @date 2026.08.19 + * @version V1.1.0 + * @history + * - V1.0.0, 2026.08.03, cyWu, 首次发布 + * - V1.1.0, 2026.08.19, cyWu, 对齐模组结构;废弃 JSON OTA;修 AUTHDELETE serviceid + ******************************************************************************/ + #include "system/includes.h" #include "app_config.h" -#include "asm/pwm_led.h" -#include "tone_player.h" -#include "ui_manage.h" #include "app_main.h" -#include "app_task.h" -#include "asm/charge.h" -#include "app_power_manage.h" -#include "app_charge.h" -#include "user_cfg.h" -#include "key_event_deal.h" -#include "audio.h" -#include "update.h" #include "vm.h" #include "bleproto.h" #include "bleproto_api.h" #include "ble_proto.h" +#include "usr_rtc.h" +#include "usr_le_api.h" +#include "jb_protocol.h" + #define LOG_TAG_CONST APP -#define LOG_TAG "[APP]" +#define LOG_TAG "[USR_LE]" #define LOG_ERROR_ENABLE #define LOG_DEBUG_ENABLE #define LOG_INFO_ENABLE -/* #define LOG_DUMP_ENABLE */ -#define LOG_CLI_ENABLE #include "debug.h" -#define FIRM_PACK_LEN 2048 // 128 +/* 拼 GATT 分包:业务包 ≈ bleproto头 + V2 开销 + JB 帧(<=128),1024 足够 */ +#define USR_LE_RX_BUF_LEN 1024 +#define USR_LE_V2_WRAP_OVERHEAD 32u /* V2 头开销,JB 帧最大 128 → 约 160 */ +#define USR_LE_V2_CMD_REPORT 100u /* REPORTCMD_V2 内层 cmd */ +#define USR_LE_V2_CMD_RUN 110u /* RUNCMD_V2 内层 cmd */ +#define USR_LE_JB_CMD_OFF 4u /* 55 AA | LEN_H LEN_L | CMD */ -static u8 start_data[10] = {0x55, 0xAA, 0xFE, 0x61, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00}; -static u8 end_data[2] = {0xaa, 0x55}; -static int encode_len = 0x00; +static int encode_len = 0; static u8 txbuf[384] = {0}; -static u8 rxbuf[FIRM_PACK_LEN + 1024] = {0}; -static u8 ota_source_data[FIRM_PACK_LEN * 2] = {0}; - -extern u16 usr_get_conn_service(); -extern void ble_multi_trans_disconnect(void); -extern void usr_ble_send_data(u8 *data, u16 len); /// BLE发送接口,MTU 244 -extern void usr_write_pair_info(); -extern void usr_clear_pair_info(); -extern void app_audio_volume_set(u8 value); -extern int dual_ota_app_data_deal(u32 msg, u8 *buf, u32 len); -extern int uart_tr_send_data(u8 *data, u32 len); -extern void usr_intterupt_close(); -extern void usr_jb_le_recieve_data(u8 *data, u16 len); -extern int8_t jbReportAckCheck(u16 cmd, u8 gateway_int_cnt, int32_t gateway_int0); - -void usr_ota_deal_task(); -void usr_data_ana(bleproto_appdata_desc_t *desc); -void usr_run_cmd_code_v2(ble_proto_packet_t *runcmd_par, u8 usr_msg_id); +static u8 rxbuf[USR_LE_RX_BUF_LEN] = {0}; +static u8 s_last_runcmd_msgid = 0; /* RUNCMD_V2 应答须回填最近 msgid */ bleproto_authsetup_req_t usr_auth_data; -void usr_le_data_recieve(u8 *data, u16 len) /// BLE数据接收 -{ - // log_info("le recieve: %d",len); - // printf_buf(data, len); - static u16 usr_len = 0; - bleproto_appdata_desc_t desc; +extern void usr_ble_send_data(u8 *data, u16 len); +extern u8 usr_ble_is_notify_ready(void); +extern void usr_write_pair_info(void); +extern void usr_clear_pair_info(void); +extern void usr_jb_le_recieve_data(u8 *data, u16 len); +extern int8_t jbReportAckCheck(u16 cmd, u8 gateway_int_cnt, int32_t gateway_int0); +static void usr_data_ana(bleproto_appdata_desc_t *desc); +static void usr_run_cmd_code_v2(ble_proto_packet_t *runcmd_par, u8 usr_msg_id); + +/** + * @brief 填 bleproto 应答头 + * @param out 输出 desc + * @param in 请求 desc(取 msgid) + * @param serviceid 服务号 + */ +static void usr_le_fill_rsp_header(bleproto_appdata_desc_t *out, + const bleproto_appdata_desc_t *in, + uint8_t serviceid) +{ + out->header.version = 0; + out->header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON; + out->header.msgtype = BLEPROTO_APPDATA_MSGTYPE_RSP; + out->header.msgid = in->header.msgid; + out->header.encrypt = 0; + out->header.serviceid = serviceid; +} + +/** + * @brief 编码并经 BLE 发出 + * @param desc 已填好的应答 + */ +static void usr_le_encode_and_send(bleproto_appdata_desc_t *desc) +{ + encode_len = bleproto_appdata_encode(txbuf, sizeof(txbuf), sizeof(txbuf), desc); + if (encode_len > 0) + { + printf("encode_len = %d\n", encode_len); + usr_ble_send_data(txbuf, encode_len); + } + else + { + printf("encode_len fail %d\n", encode_len); + } +} + +/** + * @brief 是否走网关主动上报(REPORTCMD_V2) + * @param jb_cmd 55AA 命令字 + * @return 1=主动上报,0=RUNCMD 应答 + */ +static uint8_t usr_le_is_gateway_report_cmd(uint8_t jb_cmd) +{ + return (jb_cmd == CMD_REPORT || + jb_cmd == CMD_OTA_COMPLETE || + jb_cmd == CMD_OTA_STOP_MCU) + ? 1u + : 0u; +} + +/** + * @brief BLE 收包入口 + * @param data 收包数据 + * @param len 数据长度 + */ +void usr_le_data_recieve(u8 *data, u16 len) +{ + static u16 usr_len = 0; + /* desc 含 ycmd 等 union,放 static,避免每次收包在栈上开大块 */ + static bleproto_appdata_desc_t s_rx_desc; int rc_result = 0; + if (!data || len == 0) + { + return; + } + if ((u32)usr_len + len > sizeof(rxbuf)) + { + printf("### rxbuf overflow, usr_len=%u len=%u, reset\n", usr_len, len); + usr_len = 0; + return; + } + memcpy(&rxbuf[usr_len], data, len); usr_len += len; - rc_result = bleproto_appdata_decode(rxbuf, usr_len, &desc); - // printf("rc_result_le## = %d\n",rc_result); + rc_result = bleproto_appdata_decode(rxbuf, usr_len, &s_rx_desc); if (rc_result < 0) { - // 接收到了非法的数据包,无法解析 printf("### invild pack\n"); usr_len = 0; } else if (rc_result == 0) { - // 接收的数据不完整,继续接收数据 printf("### keep recieving\n"); } - else //(rc_result > 0) + else { - // 数据解析成功 printf("### pack recieved ok\n"); usr_len = 0; - usr_data_ana(&desc); + usr_data_ana(&s_rx_desc); } } -void usr_data_ana(bleproto_appdata_desc_t *desc) +/** + * @brief 解析 bleproto 服务并分发 + * @param desc 已 decode 的包 + */ +static void usr_data_ana(bleproto_appdata_desc_t *desc) { - bleproto_appdata_desc_t encode_desc = {0}; - printf("#############334 desc->header.serviceid = %d\n", desc->header.serviceid); - if (desc->header.msgtype /*desc.header.msgtype*/ == BLEPROTO_APPDATA_MSGTYPE_REQ) + static bleproto_appdata_desc_t s_encode_desc; + + memset(&s_encode_desc, 0, sizeof(s_encode_desc)); + printf("desc serviceid=%d msgtype=%d\n", + desc->header.serviceid, desc->header.msgtype); + + if (desc->header.msgtype == BLEPROTO_APPDATA_MSGTYPE_REQ) { switch (desc->header.serviceid) { + case E_BLEPROTO_SERVICE_ID_DEVICEINFO: { + bleproto_deviceinfo_req_t *req = &desc->datast.deviceinfo_req; + bleproto_deviceinfo_rsp_t *rsp = &s_encode_desc.datast.deviceinfo_rsp; - case E_BLEPROTO_SERVICE_ID_DEVICEINFO: + /* 网关从子设备连上开始计时,每 12 小时经 DEVICEINFO 同步一次 */ + usr_rtc_set_from_unix(req->t, (int32_t)req->gmtoff); - encode_desc.header.version = 0; - encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON; - encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_RSP; - encode_desc.header.msgid = desc->header.msgid; // 这里的msg_id为上面解析到的包的msg_id; - encode_desc.header.encrypt = 0; - encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_DEVICEINFO; - - // deviceinfo rsp序列化 - bleproto_deviceinfo_rsp_t *rsp = &encode_desc.datast.deviceinfo_rsp; - { - // 填写设备相关信息 - rsp->hbcycle = 1000; - strcpy(rsp->swv, "V1.0.0.00"); - strcpy(rsp->hwv, "V1.0.0.00"); - rsp->longcon = 1; - rsp->prototype = 4; - } - encode_len = bleproto_appdata_encode(txbuf, sizeof(txbuf), sizeof(txbuf), &encode_desc); - if (encode_len > 0) - { - printf("encode_len = %d\n", encode_len); - // 调用蓝牙协议栈发送数据接口,将数据一包一包的发送出去 - // 第一包:packetlen - // 第二包:packetlen - // ...... - // 第N包: totallen - (packetlen * (N -1)) - usr_ble_send_data(txbuf, encode_len); - } - else - { - printf("2encode_len = %d\n", encode_len); - } - // printf("E_BLEPROTO_SERVICE_ID_DEVICEINFO\n"); + usr_le_fill_rsp_header(&s_encode_desc, desc, E_BLEPROTO_SERVICE_ID_DEVICEINFO); + rsp->hbcycle = 1000; + strcpy(rsp->swv, "V1.0.0.00"); + strcpy(rsp->hwv, "V1.0.0.00"); + rsp->longcon = 1; + rsp->prototype = 4; + usr_le_encode_and_send(&s_encode_desc); break; - case E_BLEPROTO_SERVICE_ID_AUTHSETUP: - // 设备处理,回复响应 + } + case E_BLEPROTO_SERVICE_ID_AUTHSETUP: { + bleproto_authsetup_rsp_t *au_rsp = &s_encode_desc.datast.authsetup_rsp; + printf("E_BLEPROTO_SERVICE_ID_AUTHSETUP\n"); - encode_desc.header.version = 0; - encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON; - encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_RSP; - encode_desc.header.msgid = desc->header.msgid; // 这里的msg_id为上面解析到的包的msg_id; - encode_desc.header.encrypt = 0; - encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_AUTHSETUP; - - bleproto_authsetup_rsp_t *au_rsp = &encode_desc.datast.authsetup_rsp; + usr_le_fill_rsp_header(&s_encode_desc, desc, E_BLEPROTO_SERVICE_ID_AUTHSETUP); au_rsp->errcode = 0; - memcpy(&usr_auth_data, &desc->datast.authsetup_req, sizeof(usr_auth_data)); + if (usr_auth_data.paired_flag == 0) { - // 保存三元组 - if (usr_auth_data.paired_flag == 0) - { - usr_write_pair_info(); - printf("usr_auth_data %s %s ", usr_auth_data.authcode, usr_auth_data.devid); - printf_buf(usr_auth_data.gwmac, 6); - } - } - - encode_len = bleproto_appdata_encode(txbuf, sizeof(txbuf), sizeof(txbuf), &encode_desc); - if (encode_len > 0) - { - printf("encode_len = %d\n", encode_len); - usr_ble_send_data(txbuf, encode_len); - } - else - { - printf("2encode_len = %d\n", encode_len); + usr_write_pair_info(); + printf("usr_auth_data %s %s ", usr_auth_data.authcode, usr_auth_data.devid); + printf_buf(usr_auth_data.gwmac, 6); } + usr_le_encode_and_send(&s_encode_desc); break; - case E_BLEPROTO_SERVICE_ID_AUTHDELETE: - // 设备处理,回复响应 - printf("E_BLEPROTO_SERVICE_ID_AUTHDELETE\n"); - encode_desc.header.version = 0; - encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON; - encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_RSP; - encode_desc.header.msgid = desc->header.msgid; // 这里的msg_id为上面解析到的包的msg_id; - encode_desc.header.encrypt = 0; - encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_AUTHSETUP; + } + case E_BLEPROTO_SERVICE_ID_AUTHDELETE: { + bleproto_authdelete_rsp_t *aude_rsp = &s_encode_desc.datast.authdelete_rsp; - bleproto_authdelete_rsp_t *aude_rsp = &encode_desc.datast.authdelete_rsp; + printf("E_BLEPROTO_SERVICE_ID_AUTHDELETE\n"); + usr_le_fill_rsp_header(&s_encode_desc, desc, E_BLEPROTO_SERVICE_ID_AUTHDELETE); memcpy(aude_rsp->gwmac, usr_auth_data.gwmac, 6); if (!memcmp(usr_auth_data.authcode, desc->datast.authdelete_req.authcode, 32)) { @@ -186,20 +207,10 @@ void usr_data_ana(bleproto_appdata_desc_t *desc) { aude_rsp->errcode = 501; } - - encode_len = bleproto_appdata_encode(txbuf, sizeof(txbuf), sizeof(txbuf), &encode_desc); - if (encode_len > 0) - { - printf("encode_len = %d\n", encode_len); - usr_ble_send_data(txbuf, encode_len); - } - else - { - printf("2encode_len = %d\n", encode_len); - } + usr_le_encode_and_send(&s_encode_desc); break; + } case E_BLEPROTO_SERVICE_ID_RUNCMD: - // 设备处理,回复响应 printf("E_BLEPROTO_SERVICE_ID_RUNCMD\n"); break; case E_BLEPROTO_SERVICE_ID_RUNCMD_V2: @@ -207,108 +218,31 @@ void usr_data_ana(bleproto_appdata_desc_t *desc) usr_run_cmd_code_v2(&desc->datast.runcmd_v2_req, desc->header.msgid); break; case E_BLEPROTO_SERVICE_ID_OTANOTIFY: - printf("E_BLEPROTO_SERVICE_ID_OTANOTIFY\n"); - encode_desc.header.version = 0; - encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON; - encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_RSP; - encode_desc.header.msgid = desc->header.msgid; // 这里的msg_id为上面解析到的包的msg_id; - encode_desc.header.encrypt = 0; - encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_OTANOTIFY; - encode_desc.datast.doaction_rsp.errcode = 0; - - bleproto_otanotify_rsp_t *in_ota_rsp = &encode_desc.datast.otanotify_rsp; - - bleproto_otanotify_req_t *in_ota_data2 = &desc->datast.otanotify_req; - printf("in_ota_data id = %d\n", in_ota_data2->id); - printf("in_ota_data ver = %s\n", in_ota_data2->version); - printf("in_ota_data size= %d\n", in_ota_data2->size); - printf("in_ota_data md5 = %s\n", in_ota_data2->md5); - { - in_ota_rsp->errcode = 0; - // 执行命令 - } - - usr_var.usr_ota_data_id = in_ota_data2->id; // int - usr_var.usr_ota_data_size = in_ota_data2->size; - encode_len = bleproto_appdata_encode(txbuf, sizeof(txbuf), sizeof(txbuf), &encode_desc); - if (encode_len > 0) - { - printf("encode_len = %d\n", encode_len); - usr_ble_send_data(txbuf, encode_len); - } - else - { - printf("2encode_len = %d\n", encode_len); - } - - usr_var.usr_allow_to_flash = 0; - usr_var.usr_data_index = 0; - start_data[2] = usr_var.usr_ota_data_size & 0x000000ff; - start_data[3] = (usr_var.usr_ota_data_size >> 8) & 0x000000ff; - start_data[4] = (usr_var.usr_ota_data_size >> 16) & 0x000000ff; - start_data[5] = (usr_var.usr_ota_data_size >> 24) & 0x000000ff; - dual_ota_app_data_deal(0, start_data, 10); - printf("#### buf total size = %d\n", usr_var.usr_ota_data_size); - printf_buf(start_data, 10); - - encode_desc.header.version = 0; - encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON; - encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_REQ; - encode_desc.header.msgid = 0; // desc->header.msgid; - encode_desc.header.encrypt = 0; - encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_OTADATA; - encode_desc.datast.doaction_rsp.errcode = 0; - - encode_desc.datast.otadata_req.id = usr_var.usr_ota_data_id; - encode_desc.datast.otadata_req.offset = usr_var.usr_data_index; - encode_desc.datast.otadata_req.maxsize = FIRM_PACK_LEN; - encode_len = bleproto_appdata_encode(txbuf, sizeof(txbuf), sizeof(txbuf), &encode_desc); - if (encode_len > 0) - { - usr_ble_send_data(txbuf, encode_len); - } - else - { - printf("2encode_len err = %d\n", encode_len); - } - - if (usr_var.usr_double_ota_timer_id == 0) - { - clk_set("sys", 48 * 1000000L); - usr_var.usr_double_ota_timer_id = 1; - // clk_set("sys", 120 * 1000000L); - os_task_create(usr_ota_deal_task, NULL, 31, FIRM_PACK_LEN, 0, "usr_ota"); - } - - break; case E_BLEPROTO_SERVICE_ID_OTADATA: - printf("E_BLEPROTO_SERVICE_ID_OTADATA\n"); - break; case E_BLEPROTO_SERVICE_ID_OTADATA_V2: - printf("E_BLEPROTO_SERVICE_ID_OTADATA\n"); + /* JSON OTA 已废弃,SOC 走 JB 0x30/0x32 */ + printf("JSON OTA deprecated, use JB 0x30/0x32\n"); break; - default: - printf("err !!! default event desc->header.serviceid =%d\n", desc->header.serviceid); + printf("err !!! default event desc->header.serviceid =%d\n", + desc->header.serviceid); break; } + return; } - else if (desc->header.msgtype == BLEPROTO_APPDATA_MSGTYPE_RSP) + + if (desc->header.msgtype == BLEPROTO_APPDATA_MSGTYPE_RSP) { - printf("BLEPROTO_APPDATA_MSGTYPE_RSP\n"); switch (desc->header.serviceid) { case E_BLEPROTO_SERVICE_ID_REPORTCMD: - // 主控回复的响应,设备处理 - printf("E_BLEPROTO_SERVICE_ID_REPORTCMD\n"); - printf("cmd id = %d", desc->datast.reportcmd_rsp.errcode); + printf("E_BLEPROTO_SERVICE_ID_REPORTCMD errcode=%d\n", + desc->datast.reportcmd_rsp.errcode); break; - case E_BLEPROTO_SERVICE_ID_REPORTCMD_V2: - { - /* 网关对主动上报 / OTA 的应答(非 55AA): - * cmd==0 && gateway_int_cnt==1 && gateway_int[0]==0 → 应答成功 */ + case E_BLEPROTO_SERVICE_ID_REPORTCMD_V2: { ble_proto_packet_t *rpt_ack = &desc->datast.reportcmd_v2_rsp; - printf("E_BLEPROTO_SERVICE_ID_REPORTCMD_V2 cmd=%u cnt=%u int0=%ld\n", + + printf("REPORTCMD_V2 cmd=%u cnt=%u int0=%ld\n", (unsigned)rpt_ack->cmd, (unsigned)rpt_ack->gateway_int_cnt, (long)((rpt_ack->gateway_int_cnt > 0) ? rpt_ack->gateway_int[0] : 0)); @@ -318,120 +252,23 @@ void usr_data_ana(bleproto_appdata_desc_t *desc) break; } case E_BLEPROTO_SERVICE_ID_OTADATA: - bleproto_otadata_rsp_t *ota_data_rsp = &desc->datast.otadata_rsp; - // printf("errcode = %d offset = %d len = %d\n",ota_data_rsp->errcode,ota_data_rsp->offset,ota_data_rsp->len); - uart_tr_send_data(ota_data_rsp->data, ota_data_rsp->len); - memcpy(&ota_source_data[usr_var.usr_allow_to_flash], ota_data_rsp->data, ota_data_rsp->len); - usr_var.usr_allow_to_flash += ota_data_rsp->len; - usr_var.usr_start_write_flash = 0; - if (usr_var.usr_allow_to_flash >= (FIRM_PACK_LEN * 2)) // 0 - 1024 - // 1024 - 2048 - // 2048 - 3072 - // 3072 - 4096 - { - usr_var.usr_start_write_flash = 1; - } - else - { - usr_var.usr_data_index += FIRM_PACK_LEN; - if (usr_var.usr_data_index >= usr_var.usr_ota_data_size) - { - usr_var.usr_start_write_flash = 2; - break; - } - // bleproto_appdata_desc_t encode_desc = { 0 }; - encode_desc.header.version = 0; - encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON; - encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_REQ; - encode_desc.header.msgid = 0; // desc->header.msgid; - encode_desc.header.encrypt = 0; - encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_OTADATA; - encode_desc.datast.doaction_rsp.errcode = 0; - - encode_desc.datast.otadata_req.id = usr_var.usr_ota_data_id; - encode_desc.datast.otadata_req.offset = usr_var.usr_data_index; - encode_desc.datast.otadata_req.maxsize = FIRM_PACK_LEN; - encode_len = bleproto_appdata_encode(txbuf, sizeof(txbuf), sizeof(txbuf), &encode_desc); - if (encode_len > 0) - { - usr_ble_send_data(txbuf, encode_len); - } - else - { - printf("2encode_len err = %d\n", encode_len); - } - } + printf("JSON OTA deprecated, use JB 0x32\n"); break; default: - printf("E_BLEPROTO_SERVICE_ID_REPORTCMD\n"); + printf("unhandled RSP serviceid=%d\n", desc->header.serviceid); break; } } } -void usr_ota_deal_task() +/** + * @brief APP RUNCMD_V2:55AA 字节流转交 JB 协议层 + * @param runcmd_par 内层包 + * @param usr_msg_id bleproto msgid + */ +static void usr_run_cmd_code_v2(ble_proto_packet_t *runcmd_par, u8 usr_msg_id) { - os_time_dly(5); - bleproto_appdata_desc_t encode_desc = {0}; - while (1) - { - if (usr_var.usr_ota_succ_flag == 0) - { - if (usr_var.usr_start_write_flash == 1) - { - // OS_ENTER_CRITICAL(); - usr_var.usr_data_index += FIRM_PACK_LEN; - usr_var.usr_start_write_flash = 0; - dual_ota_app_data_deal(0, ota_source_data, usr_var.usr_allow_to_flash); - os_time_dly(1); - // OS_EXIT_CRITICAL(); - // printf("usr_var.usr_allow_to_flash = %d\n",usr_var.usr_allow_to_flash); - usr_var.usr_allow_to_flash = 0; - - encode_desc.header.version = 0; - encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON; - encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_REQ; - encode_desc.header.msgid = 0; // desc->header.msgid; - encode_desc.header.encrypt = 0; - encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_OTADATA; - encode_desc.datast.doaction_rsp.errcode = 0; - - encode_desc.datast.otadata_req.id = usr_var.usr_ota_data_id; - encode_desc.datast.otadata_req.offset = usr_var.usr_data_index; - encode_desc.datast.otadata_req.maxsize = FIRM_PACK_LEN; - encode_len = bleproto_appdata_encode(txbuf, sizeof(txbuf), sizeof(txbuf), &encode_desc); - if (encode_len > 0) - { - usr_ble_send_data(txbuf, encode_len); - } - else - { - printf("2encode_len err = %d\n", encode_len); - } - } - else if (usr_var.usr_start_write_flash == 2) - { - printf("usr_start_write_flash = %d\n", usr_var.usr_start_write_flash); - usr_var.usr_start_write_flash = 0; - // OS_ENTER_CRITICAL(); - dual_ota_app_data_deal(0, ota_source_data, usr_var.usr_allow_to_flash); - // OS_EXIT_CRITICAL(); - printf("usr_var.usr_allow_to_flash = %d\n", usr_var.usr_allow_to_flash); - usr_var.usr_allow_to_flash = 0; - os_time_dly(30); - dual_ota_app_data_deal(0, end_data, 2); - } - } - os_time_dly(10); - // putchar('6'); - } -} - -/* 最近一次收到 RUNCMD_V2 请求的 msgid,应答时回填 */ -static u8 s_last_runcmd_msgid = 0; -void usr_run_cmd_code_v2(ble_proto_packet_t *runcmd_par, u8 usr_msg_id) -{ - u8 jb_frame[128]; /* 与 jb_protocol.h MAX_PACKAGE_LEN 一致 */ + u8 jb_frame[MAX_PACKAGE_LEN]; u16 copy_len; if (!runcmd_par || !runcmd_par->bytes_data || runcmd_par->device_bytes_len == 0) @@ -444,7 +281,6 @@ void usr_run_cmd_code_v2(ble_proto_packet_t *runcmd_par, u8 usr_msg_id) printf("runcmd_par id = %d, bytes_len = %u\n", runcmd_par->cmd, runcmd_par->device_bytes_len); - /* 先拷到本地再入 JB 环缓,避免零拷贝指针被后续逻辑覆盖 */ copy_len = runcmd_par->device_bytes_len; if (copy_len > sizeof(jb_frame)) { @@ -461,16 +297,17 @@ void usr_run_cmd_code_v2(ble_proto_packet_t *runcmd_par, u8 usr_msg_id) * @brief 将 JB 协议帧封装为 bleproto V2 后通过 BLE 发送 * @param data JB 协议原始帧(55 AA | LEN | CMD | ...) * @param len 帧长度 - * @note 根据 JB CMD 区分主动上报 / 应答: - * - 主动(0x07/0x34/0x38): REPORTCMD_V2 + REQ + cmd=100 - * - 应答(其余): RUNCMD_V2 + RSP + cmd=110 + * @note 主动上报(0x07/0x34/0x38) → REPORTCMD_V2;其余 → RUNCMD_V2 应答 + * 对外符号名保持 ble_send_data,供 jb_protocol 调用 */ void ble_send_data(u8 *data, u16 len) { - bleproto_appdata_desc_t encode_desc = {0}; - ble_proto_packet_t *pkt = NULL; + ble_proto_packet_t pkt; + bleproto_appdata_header_t hdr; + u8 v2_buf[MAX_PACKAGE_LEN + USR_LE_V2_WRAP_OVERHEAD]; u8 jb_cmd; u8 is_active_send; + int plen; int elen; if (!data || len < 5) @@ -478,54 +315,62 @@ void ble_send_data(u8 *data, u16 len) printf("ble_send_data invalid param, len=%d\n", len); return; } + if (len > MAX_PACKAGE_LEN) + { + printf("ble_send_data frame too long %u\n", len); + return; + } + if (!usr_ble_is_notify_ready()) + { + printf("ble_send_data wait CCC cmd=0x%02X\n", data[USR_LE_JB_CMD_OFF]); + return; + } - /* JB帧: 55 AA | LEN_H LEN_L | CMD | SN | ...,CMD 在偏移 4 */ - jb_cmd = data[4]; - /* 主动上报 0x07;MCU 主动 OTA 完成/停止 0x34/0x38;其余按应答 */ - is_active_send = (jb_cmd == 0x07) || (jb_cmd == 0x34) || (jb_cmd == 0x38); + jb_cmd = data[USR_LE_JB_CMD_OFF]; + is_active_send = usr_le_is_gateway_report_cmd(jb_cmd); - encode_desc.header.version = 0; - encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON; - encode_desc.header.encrypt = 0; + memset(&pkt, 0, sizeof(pkt)); + memset(&hdr, 0, sizeof(hdr)); + hdr.version = 0; + hdr.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON; + hdr.encrypt = 0; if (is_active_send) { - /* 主动发送:REPORTCMD_V2 / REQ / cmd=100 */ - encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_REQ; - encode_desc.header.msgid = 0; - encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_REPORTCMD_V2; - - pkt = &encode_desc.datast.reportcmd_v2_req; - pkt->cmd = 100; - pkt->reserved = 0; - pkt->gateway_int_cnt = 0; + hdr.msgtype = BLEPROTO_APPDATA_MSGTYPE_REQ; + hdr.msgid = 0; + hdr.serviceid = E_BLEPROTO_SERVICE_ID_REPORTCMD_V2; + pkt.cmd = USR_LE_V2_CMD_REPORT; + pkt.gateway_int_cnt = 0; } else { - /* 应答命令:RUNCMD_V2 / RSP / cmd=110,回填最新 msgid */ - encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_RSP; - encode_desc.header.msgid = s_last_runcmd_msgid; - encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_RUNCMD_V2; - - pkt = &encode_desc.datast.runcmd_v2_rsp; - pkt->cmd = 110; - pkt->reserved = 0; - pkt->gateway_int_cnt = 2; - pkt->gateway_int[0] = 0; - pkt->gateway_int[1] = 0; + hdr.msgtype = BLEPROTO_APPDATA_MSGTYPE_RSP; + hdr.msgid = s_last_runcmd_msgid; + hdr.serviceid = E_BLEPROTO_SERVICE_ID_RUNCMD_V2; + pkt.cmd = USR_LE_V2_CMD_RUN; + pkt.gateway_int_cnt = 2; + pkt.gateway_int[0] = 0; + pkt.gateway_int[1] = 0; } - pkt->device_int_len = 0; - pkt->device_str_len = 0; - pkt->device_bytes_len = len; - pkt->bytes_data = data; + pkt.device_bytes_len = len; + pkt.bytes_data = data; - elen = bleproto_appdata_encode(txbuf, sizeof(txbuf), sizeof(txbuf), &encode_desc); - printf("ble_send_data jb_cmd=0x%02X active=%d encode_len=%d msgid=%d\n", - jb_cmd, is_active_send, elen, encode_desc.header.msgid); - if (elen > 0) + plen = ble_proto_pack(&pkt, v2_buf, sizeof(v2_buf)); + if (plen <= 0) { - usr_ble_send_data(txbuf, elen); - printf_buf(txbuf, elen); + printf("ble_send_data pack fail %d\n", plen); + return; } + + elen = bleproto_appdata_wrap_raw(txbuf, sizeof(txbuf), &hdr, v2_buf, (uint16_t)plen); + printf("ble_send_data jb_cmd=0x%02X active=%d encode_len=%d msgid=%d\n", + jb_cmd, is_active_send, elen, hdr.msgid); + if (elen <= 0) + { + return; + } + usr_ble_send_data(txbuf, elen); + printf_buf(txbuf, elen); } diff --git a/apps/usr_periph/usr_rtc.c b/apps/usr_periph/usr_rtc.c new file mode 100644 index 0000000..d44dbe4 --- /dev/null +++ b/apps/usr_periph/usr_rtc.c @@ -0,0 +1,363 @@ +/****************************************************************************** + * @file usr_rtc.c + * @brief 板级 RTC 外设实现(BR28 硬件 rtc_dev_ops) + * @author cyWu <1917507415@qq.com> + * @date 2026.08.19 + * @version V1.1.0 + * @history + * - V1.0.0, 2026.08.19, cyWu, 从模组固件移植至 SOC SDK + * - V1.1.0, 2026.08.19, cyWu, 精简公共接口与缓存逻辑 + ******************************************************************************/ + +#include "system/includes.h" +#include "app_config.h" +#include "usr_rtc.h" +#include "asm/rtc.h" + +#define LOG_TAG_CONST APP +#define LOG_TAG "[USR_RTC]" +#define LOG_ERROR_ENABLE +#define LOG_DEBUG_ENABLE +#define LOG_INFO_ENABLE +#include "debug.h" + +/*============================================================================*/ +/* 私有宏 / 类型 */ +/*============================================================================*/ + +#define USR_RTC_TZ_MAGIC (0x52544331u) /**< 'RTC1',校验 VM 时区 */ + +typedef struct { + uint32_t magic; + int32_t tz_s; +} UsrRtcTzVm_t; + +typedef struct { + uint8_t inited; + uint8_t tz_valid; + int32_t tz_s; + void *dev; +} UsrRtc_t; + +/*============================================================================*/ +/* 私有变量 */ +/*============================================================================*/ + +static UsrRtc_t s_rtc; + +/*============================================================================*/ +/* 私有函数声明 */ +/*============================================================================*/ + +static uint8_t usr_rtc_is_leap(uint16_t year); +static uint8_t usr_rtc_days_in_month(uint16_t year, uint8_t month); +static uint8_t usr_rtc_year_ok(uint16_t year); +static void usr_rtc_unix_to_calendar(uint32_t unix_utc, int32_t tz_s, + struct sys_time *t); +static UsrRtc_Ret_t usr_rtc_write(const struct sys_time *t); +static void usr_rtc_load_tz(void); +static void usr_rtc_save_tz(int32_t tz_s); + +/*============================================================================*/ +/* 公有函数 */ +/*============================================================================*/ + +/** + * @brief 打开 rtc 设备并加载时区 + * @return UsrRtc_Ret_t + */ +UsrRtc_Ret_t usr_rtc_init(void) +{ + struct sys_time t; + + if (s_rtc.inited) + { + return USR_RTC_OK; + } + +#if TCFG_APP_RTC_EN + s_rtc.dev = dev_open("rtc", NULL); + if (!s_rtc.dev) + { + log_error("open rtc fail"); + return USR_RTC_ERR_NOT_INIT; + } +#else + log_error("TCFG_APP_RTC_EN=0"); + return USR_RTC_ERR_NOT_INIT; +#endif + + usr_rtc_load_tz(); + s_rtc.inited = 1; + + memset(&t, 0, sizeof(t)); + read_sys_time(&t); + log_info("rtc open %u-%02u-%02u %02u:%02u:%02u tz=%ld tz_ok=%u", + t.year, t.month, t.day, t.hour, t.min, t.sec, + (long)s_rtc.tz_s, s_rtc.tz_valid); + return USR_RTC_OK; +} + +/** + * @brief 获取当前本地时间 + * @param t 成功时赋值;失败不改动 + * @return 1/0 + */ +uint8_t usr_rtc_get_time(struct sys_time *t) +{ + struct sys_time tmp; + + if (!t || !s_rtc.inited || !s_rtc.tz_valid) + { + return 0; + } + + memset(&tmp, 0, sizeof(tmp)); + read_sys_time(&tmp); + if (!usr_rtc_year_ok(tmp.year)) + { + return 0; + } + + *t = tmp; + return 1; +} + +/** + * @brief UTC 秒 + 时区 → 本地日历写入 RTC + * @param unix_utc UTC 秒 + * @param tz_s 时区偏移秒 + * @return UsrRtc_Ret_t + */ +UsrRtc_Ret_t usr_rtc_set_from_unix(uint64_t unix_utc, int32_t tz_s) +{ + struct sys_time t; + UsrRtc_Ret_t ret; + + if (!s_rtc.inited) + { + return USR_RTC_ERR_NOT_INIT; + } + if (unix_utc == 0) + { + log_debug("unix=0, ignore"); + return USR_RTC_ERR_PARAM; + } + + /* 网关采样到写入大约慢 1s,在 UTC 上补偿 */ + unix_utc += USR_RTC_SYNC_COMPENSATE_S; + if ((tz_s > USR_RTC_TZ_ABS_MAX_S) || (tz_s < -USR_RTC_TZ_ABS_MAX_S)) + { + log_error("tz out of range %ld", (long)tz_s); + return USR_RTC_ERR_PARAM; + } + if (unix_utc > 0xFFFFFFFFull) + { + log_error("unix too large"); + return USR_RTC_ERR_PARAM; + } + + usr_rtc_unix_to_calendar((uint32_t)unix_utc, tz_s, &t); + if (!usr_rtc_year_ok(t.year)) + { + log_error("year %u not in %u~%u, wait sync", + t.year, USR_RTC_YEAR_MIN, USR_RTC_YEAR_MAX); + return USR_RTC_ERR_INVALID; + } + + ret = usr_rtc_write(&t); + if (ret != USR_RTC_OK) + { + return ret; + } + + usr_rtc_save_tz(tz_s); + log_info("set rtc %u-%02u-%02u %02u:%02u:%02u tz=%ld", + t.year, t.month, t.day, t.hour, t.min, t.sec, (long)tz_s); + return USR_RTC_OK; +} + +/*============================================================================*/ +/* 私有函数 */ +/*============================================================================*/ + +/** + * @brief 写硬件日历 + * @param t 本地日历 + * @return UsrRtc_Ret_t + */ +static UsrRtc_Ret_t usr_rtc_write(const struct sys_time *t) +{ + struct sys_time tmp; + + if (!t) + { + return USR_RTC_ERR_PARAM; + } + if (!s_rtc.inited) + { + return USR_RTC_ERR_NOT_INIT; + } + if (!usr_rtc_year_ok(t->year) || (t->month < 1) || (t->month > 12) || + (t->day < 1) || (t->hour > 23) || (t->min > 59) || (t->sec > 59)) + { + return USR_RTC_ERR_PARAM; + } + if (t->day > usr_rtc_days_in_month(t->year, t->month)) + { + return USR_RTC_ERR_PARAM; + } + + tmp = *t; + write_sys_time(&tmp); + return USR_RTC_OK; +} + +/** + * @brief 是否闰年 + * @param year 公历年 + * @return 1/0 + */ +static uint8_t usr_rtc_is_leap(uint16_t year) +{ + if ((year % 400u) == 0) + { + return 1; + } + if ((year % 100u) == 0) + { + return 0; + } + return ((year % 4u) == 0) ? 1 : 0; +} + +/** + * @brief 某月天数 + * @param year 年 + * @param month 月 1~12 + * @return 天数,非法月返回 0 + */ +static uint8_t usr_rtc_days_in_month(uint16_t year, uint8_t month) +{ + static const uint8_t days[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + if ((month < 1) || (month > 12)) + { + return 0; + } + if ((month == 2) && usr_rtc_is_leap(year)) + { + return 29; + } + return days[month - 1]; +} + +/** + * @brief 年份是否在合法窗口 + * @param year 年 + * @return 1/0 + */ +static uint8_t usr_rtc_year_ok(uint16_t year) +{ + return ((year >= USR_RTC_YEAR_MIN) && (year <= USR_RTC_YEAR_MAX)) ? 1 : 0; +} + +/** + * @brief UTC 秒 + 时区 → 本地日历 + * @param unix_utc UTC 秒 + * @param tz_s 时区偏移 + * @param t 输出 + */ +static void usr_rtc_unix_to_calendar(uint32_t unix_utc, int32_t tz_s, + struct sys_time *t) +{ + int64_t local; + uint32_t days; + uint32_t tod; + uint16_t y; + uint8_t m; + uint8_t dim; + + memset(t, 0, sizeof(*t)); + local = (int64_t)unix_utc + (int64_t)tz_s; + if (local < 0) + { + local = 0; + } + + tod = (uint32_t)(local % 86400); + days = (uint32_t)(local / 86400); + t->sec = (uint8_t)(tod % 60u); + t->min = (uint8_t)((tod / 60u) % 60u); + t->hour = (uint8_t)(tod / 3600u); + + y = 1970; + while (1) + { + uint32_t diy = usr_rtc_is_leap(y) ? 366u : 365u; + if (days < diy) + { + break; + } + days -= diy; + y++; + if (y > 4095u) + { + break; + } + } + t->year = y; + + for (m = 1; m <= 12u; m++) + { + dim = usr_rtc_days_in_month(y, m); + if (days < dim) + { + break; + } + days -= dim; + } + t->month = m; + t->day = (uint8_t)(days + 1u); +} + +/** + * @brief 从 VM 加载时区 + */ +static void usr_rtc_load_tz(void) +{ + UsrRtcTzVm_t vm; + int ret; + + s_rtc.tz_valid = 0; + s_rtc.tz_s = 0; + memset(&vm, 0, sizeof(vm)); + ret = syscfg_read(CFG_USER_TIM_FLAG, (u8 *)&vm, sizeof(vm)); + if ((ret == (int)sizeof(vm)) && (vm.magic == USR_RTC_TZ_MAGIC)) + { + if ((vm.tz_s <= USR_RTC_TZ_ABS_MAX_S) && (vm.tz_s >= -USR_RTC_TZ_ABS_MAX_S)) + { + s_rtc.tz_s = vm.tz_s; + s_rtc.tz_valid = 1; + log_info("tz from vm %ld", (long)s_rtc.tz_s); + return; + } + } + log_debug("tz vm empty"); +} + +/** + * @brief 时区写入 VM + * @param tz_s 时区偏移秒 + */ +static void usr_rtc_save_tz(int32_t tz_s) +{ + UsrRtcTzVm_t vm; + + s_rtc.tz_s = tz_s; + s_rtc.tz_valid = 1; + vm.magic = USR_RTC_TZ_MAGIC; + vm.tz_s = tz_s; + syscfg_write(CFG_USER_TIM_FLAG, (u8 *)&vm, sizeof(vm)); +} diff --git a/apps/usr_periph/usr_rtc.h b/apps/usr_periph/usr_rtc.h new file mode 100644 index 0000000..9c5de7e --- /dev/null +++ b/apps/usr_periph/usr_rtc.h @@ -0,0 +1,64 @@ +/****************************************************************************** + * @file usr_rtc.h + * @brief 板级 RTC:硬件对时 + 客户读本地日历 + * @author cyWu <1917507415@qq.com> + * @date 2026.08.19 + * @version V1.1.0 + * @history + * - V1.0.0, 2026.08.19, cyWu, 从模组固件移植至 SOC SDK + * - V1.1.0, 2026.08.19, cyWu, 精简接口;usr_rtc_get_time 返回本地日历 + ******************************************************************************/ + +#ifndef __USR_RTC_H__ +#define __USR_RTC_H__ + +#include +#include "system/sys_time.h" + +/*============================================================================*/ +/* 宏定义 */ +/*============================================================================*/ + +#define USR_RTC_YEAR_MIN (2026u) /**< 合法年份下限 */ +#define USR_RTC_YEAR_MAX (2098u) /**< 合法年份上限 */ +#define USR_RTC_TZ_ABS_MAX_S (57600) /**< 时区偏移绝对值上限(±16h) */ +#define USR_RTC_SYNC_COMPENSATE_S (1u) /**< 网关对时延迟补偿(秒) */ + +/*============================================================================*/ +/* 错误码(init / set_from_unix) */ +/*============================================================================*/ + +typedef enum { + USR_RTC_OK = 0, + USR_RTC_ERR_PARAM, + USR_RTC_ERR_NOT_INIT, + USR_RTC_ERR_INVALID, /**< 年份非法,等待 App 对时 */ +} UsrRtc_Ret_t; + +/*============================================================================*/ +/* 外部接口 */ +/*============================================================================*/ + +/** + * @brief 打开 "rtc" 设备并加载时区 + * @return UsrRtc_Ret_t + * @note app_main 启动时调用一次即可 + */ +UsrRtc_Ret_t usr_rtc_init(void); + +/** + * @brief 获取当前本地时间(客户常用) + * @param t 成功时写入年月日时分秒;失败时不改动 + * @return 1=已对时且读成功,0=未初始化/未对时/参数非法 + */ +uint8_t usr_rtc_get_time(struct sys_time *t); + +/** + * @brief App/网关 DEVICEINFO:UTC 秒 + 时区写入 RTC + * @param unix_utc UTC Unix 秒;0 无效 + * @param tz_s 时区偏移秒(东八区 28800) + * @return UsrRtc_Ret_t + */ +UsrRtc_Ret_t usr_rtc_set_from_unix(uint64_t unix_utc, int32_t tz_s); + +#endif /* __USR_RTC_H__ */ diff --git a/cpu/br28/tools/download/earphone/download_app_ota.bat b/cpu/br28/tools/download/earphone/download_app_ota.bat index 1e15e05..bb583c5 100644 --- a/cpu/br28/tools/download/earphone/download_app_ota.bat +++ b/cpu/br28/tools/download/earphone/download_app_ota.bat @@ -10,10 +10,9 @@ copy ..\..\ota.bin . copy ..\..\anc_coeff.bin . copy ..\..\anc_gains.bin . -..\..\isd_download.exe ..\..\isd_config.ini -tonorflash -dev br28 -boot 0x120000 -div8 -wait 300 -uboot ..\..\uboot.boot -app ..\..\app.bin -res ..\..\cfg_tool.bin tone.cfg p11_code.bin -uboot_compress -key RS-5B88.key +..\..\isd_download.exe ..\..\isd_config.ini -tonorflash -dev br28 -boot 0x120000 -div8 -wait 300 -uboot ..\..\uboot.boot -app ..\..\app.bin -res ..\..\cfg_tool.bin tone.cfg p11_code.bin -uboot_compress - -:: -format all +:: -format all -key RS-5B88.key ::-reboot 2500 @rem ɾʱļ-format all diff --git a/include_lib/system/device/iokey.h b/include_lib/system/device/iokey.h index c9f0082..e9c7374 100644 --- a/include_lib/system/device/iokey.h +++ b/include_lib/system/device/iokey.h @@ -48,6 +48,23 @@ struct iokey_platform_data { extern int iokey_init(const struct iokey_platform_data *iokey_data); extern u8 io_get_key_value(void); +/* 开机锁键:长按开机不松手时屏蔽全部按键,避免误触长按关机。 + * 默认监视 port[0](请把电源键配成第 0 个)。 + * 松开并连续确认 release_cnt 次扫描后解锁(扫描约 10ms 一次,默认 10 ≈ 100ms)。 + * + * 关闭方式: + * 1) 编译关闭:在 board/app_config 里 #define TCFG_IOKEY_BOOT_LOCK_ENABLE 0 + * 2) 运行关闭:iokey_boot_lock_stop() 或 iokey_boot_lock_start(0) + */ +#ifndef TCFG_IOKEY_BOOT_LOCK_ENABLE +#define TCFG_IOKEY_BOOT_LOCK_ENABLE 1 +#endif +#ifndef IOKEY_BOOT_LOCK_DEFAULT_CNT +#define IOKEY_BOOT_LOCK_DEFAULT_CNT 10 +#endif +void iokey_boot_lock_start(u8 release_cnt); /* >0 启动;0=关闭 */ +void iokey_boot_lock_stop(void); /* 立即关闭锁键 */ + #endif diff --git a/tools/package_release.py b/tools/package_release.py index 18ec760..b586101 100644 --- a/tools/package_release.py +++ b/tools/package_release.py @@ -492,6 +492,7 @@ def write_readme(readme_path: Path, version: str, module_stats: List[dict]) -> N "│ ├── common/ # SDK 公共模块", "│ ├── earphone/ # 耳机应用", "│ ├── usr_jb_proto/ # 应用协议层", + "│ ├── usr_periph/ # 板级外设(RTC 等,源码开放)", "│ └── usr_le_code/ # BLE 协议库(库文件 + 公开头文件)", "├── cpu/ # 芯片相关代码与工具", "├── include_lib/ # SDK 头文件", diff --git a/version.txt b/version.txt index 3eefcb9..7dea76e 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.0.0 +1.0.1