CH582M MESH 低功耗按钮睡眠问题

我使用模板?adv_vendor_self_provision_with_peripheral 创建了工程,用来实现相关功能。目前LED灯灯设备正常,但是想要开发一个用电池供电的按钮可以实现灯具控制时,在令CH582M睡眠这里出现了问题。


我的想法是使用 vendor_message_srv_send_trans 把数据发出后等待若干时间,待数据妥善发出后再令CH582M进入睡眠状态,目前使用过Timer0定时、SysTick定时、RTC定时、32KCycle定时执行此过程,但是只要写上睡眠的代码,实际执行时不会等待,而是立即进入了睡眠,导致TMOS无法送达数据。下面是app_main.c的代码,app.c和peripheral.c中只是在接收部分执行了相应逻辑。


millis的宏定义为 #define millis() __millis

__millis为一个uint32_t全局变量


论坛的楼层更新功能有BUG,会导致空白字符显示成 ? 可能是字符集问题,代码已在下面的楼层中重新发出


因为是测试代码,所以按键使用了主循环轮询检测,没有使用中断

因为是测试代码,所以按键没有使用中断,是在主循环中使用轮询实现的



/********************************** (C) COPYRIGHT *******************************
 * File Name          : main.c
 * Author             : WCH
 * Version            : V1.1
 * Date               : 2022/01/18
 * Description        :
 *********************************************************************************
 * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
 * Attention: This software (modified or not) and binary are used for 
 * microcontroller manufactured by Nanjing Qinheng Microelectronics.
 *******************************************************************************/

/******************************************************************************/
/* 头文件包含 */

#include "CONFIG.h"
#include "MESH_LIB.h"
#include "HAL.h"
#include "app_mesh_config.h"
#include "app.h"
#include "app_vendor_model_srv.h"
#include "var.h"
#include "sha256.h"

#define ThisFirmwareIsForRemote

#ifdef  ThisFirmwareIsForRemote

volatile uint32_t pressedTime = 0;
volatile uint8_t sent = 0;
volatile uint32_t count = 0;

#endif

void Timer0_timerInit(uint32_t time_data);

#include "CH58x_common.h"
/*********************************************************************
 * GLOBAL TYPEDEFS
 */
__attribute__((aligned(4)))            uint32_t MEM_BUF[BLE_MEMHEAP_SIZE / 4];
#if(defined(BLE_MAC)) && (BLE_MAC == TRUE)
const uint8_t MacAddr[6] = {0x84, 0xC2, 0xE4, 0x03, 0x02, 0x02};
#endif

uint8_t pendingProcess = 1;

/*********************************************************************
 * @fn      Main_Circulation
 *
 * @brief   主循环
 *
 * @return  none
 */
__HIGH_CODE
__attribute__((noinline))
void Main_Circulation() {
    while(6)
    {
        TMOS_SystemProcess();

#ifdef  ThisFirmwareIsForRemote

        if(sent) {
            if(millis() > 10) {
                //LowPower_Sleep(0); // 此代码取消注释则会忽略设定的Timeout立即执行睡眠
            }
        }

        if(GPIOB_ReadPortPin(GPIO_Pin_22) == 0) {
            if(!pressedTime) {
                pressedTime = millis();
            }
        } else {
            if(pressedTime) {
                uint32_t gap = millis() - pressedTime;
                pressedTime = 0;
                if(gap > 50 && gap < 500) {
                    //printf("pressed\n");
                    //pressed

                    //重置数据发送
                    vendor_message_srv_trans_reset();
                    uint8_t status;

                    //定义数据发送参数
                    struct send_param param = {.addr = BLE_MESH_ADDR_ALL_NODES, // 此消息发往的目的地地址,例程为发往订阅地址,包括自己
                        .trans_cnt = 0x01,// 此消息的用户层发送次数
                        .period = K_MSEC(500),// 此消息重传的间隔,建议不小于(200+50*TTL)ms,若数据较大则建议加长
                        .rand = (100),// 此消息发送的随机延迟
                        .tid = vendor_srv_tid_get(),// tid,每个独立消息递增循环,srv使用128~191
                        .send_ttl = BLE_MESH_TTL_DEFAULT,// ttl,无特定则使用默认值
                    };

                    //用于存储发送的数据
                    //8字节数据 + 32字节sha256 + 2字节opIndex
                    uint8_t bufSend[MESSAGE_LENGTH + 32 + 2] = {0};

                    //需要发送的数据, 目前只使用了8字节中的4个字节
                    char cmd[4] = {'l', 'u', 'a', 'x'};

                    //发送的数据拷贝到缓冲区
                    memcpy(bufSend, cmd, 4);

                    //opIndex拷贝到数据缓冲区
                    memcpy(bufSend + MESSAGE_LENGTH + 32, &opIndex, 2);
                    ++opIndex;

                    //取数字摘要
                    uint8_t hash[32] = {0};
                    sha256(bufSend, MESSAGE_LENGTH + 34, hash);

                    //printf("SHA256 OK\n HEX: \n");
                    //for (int i = 0; i < 32; ++i) {
                    //    printf("%02x ", hash[i]);
                    //}
                    //printf("\n");

                    //拷贝数字摘要到本地暂存区
                    memcpy(hashes[hashesIndex], hash, 32);
                    ++hashesIndex;
                    if (hashesIndex >= 99) {
                        hashesIndex = 0;
                    }

                    //拷贝数字摘要到发送数据缓冲区
                    memcpy(bufSend + MESSAGE_LENGTH, hash, 32);

                    //vendor_message_srv_trans_reset();
                    //或者调用自定义模型服务的透传函数发送数据,只发送,无应答机制
                    vendor_message_srv_send_trans(&param, bufSend,
                            MESSAGE_LENGTH + 34);

                    printf("message re send to others\n");

                    //关闭Timer0中断
                    TMR0_ITCfg(DISABLE, TMR0_3_IT_CYC_END);
                    PFIC_DisableIRQ(TMR0_IRQn);

                    R8_TMR0_CTRL_MOD = RB_TMR_ALL_CLEAR;
                    R8_TMR0_CTRL_MOD &= 0b11111011;;

                    //重置计数
                    __millis = 0;

                    //设定标志
                    sent = 1;

                    //重新启动Timer0, 以1000ms为单位, 此函数为自定义的函数, 进行了时间转换
                    Timer0_timerInit(1000);

                }
            }
        }
#endif
    }
}

/*********************************************************************
 * @fn      bt_mesh_lib_init
 *
 * @brief   mesh 库初始化
 *
 * @return  state
 */
uint8_t bt_mesh_lib_init(void) {
    uint8_t ret;

    if (tmos_memcmp(VER_MESH_LIB, VER_MESH_FILE, strlen(VER_MESH_FILE)) == FALSE) {
        PRINT("mesh head file error...\n");
        while(1);
    }

    ret = RF_RoleInit();

#if((CONFIG_BLE_MESH_PROXY) ||   \
    (CONFIG_BLE_MESH_PB_GATT) || \
    (CONFIG_BLE_MESH_OTA))
    ret = GAPRole_PeripheralInit();
#endif /* PROXY || PB-GATT || OTA */

#if(CONFIG_BLE_MESH_PROXY_CLI)
    ret = GAPRole_CentralInit();
#endif /* CONFIG_BLE_MESH_PROXY_CLI */

    MeshTimer_Init();
    MeshDeamon_Init();
    ble_sm_alg_ecc_init();

#if(CONFIG_BLE_MESH_IV_UPDATE_TEST)
    bt_mesh_iv_update_test(TRUE);
#endif
    return ret;
}

void Timer0_timerInit(uint32_t time_data) {
    //定时器0
    TMR0_TimerInit(FREQ_SYS / (1000 / time_data)); // time_data为设置定时时间值,设置多少时间就等于多少时间(单位ms)

    TMR0_ITCfg(ENABLE, TMR0_3_IT_CYC_END); // 开启中断
    PFIC_EnableIRQ(TMR0_IRQn);   //使能TMR0的中断,并指定中断号为 TMR0_IRQn

}

__INTERRUPT
__HIGH_CODE
void TMR0_IRQHandler(void) // TMR0 定时中断
{
    if (TMR0_GetITFlag(TMR0_3_IT_CYC_END)) {
        TMR0_ClearITFlag(TMR0_3_IT_CYC_END); // 清除中断标志
        ++__millis;
    }
}

/*********************************************************************
 * @fn      main
 *
 * @brief   主函数
 *
 * @return  none
 */
int main(void) {
    SetSysClock(CLK_SOURCE_PLL_60MHz);

    //  自动重新加载计数值,计数时钟60M的话,以1ms为例,参数是60000
    //SysTick_Config( GetSysClock() / 1000 * SYSTICK_INTERVAL);  //设定嘀嗒时间1000ms

    PRINT("%s\n", VER_LIB);
    PRINT("%s\n", VER_MESH_LIB);

    CH58X_BLEInit();
    HAL_Init();
    bt_mesh_lib_init();
    App_Init();

    GPIOB_ModeCfg(GPIO_Pin_0, GPIO_ModeOut_PP_20mA);
    GPIOB_ModeCfg(GPIO_Pin_4, GPIO_ModeOut_PP_5mA);

    for (int i = 0; i < 16; ++i) {
        GPIOB_InverseBits(GPIO_Pin_0);
        GPIOB_InverseBits(GPIO_Pin_4);
        DelayMs(100);
    }

    printf("\nPrepare loading user config\n");
    EEPROM_READ(USER_DATA_ADDR, &userData, sizeof(userData));
    printf("User config loaded\n");
    printf("my role is: %c\n", userData.role);

    __millis = 0;
    Timer0_timerInit(1);

#ifdef  ThisFirmwareIsForRemote
    userData.remote.isRemote = 1;
    userData.remote.targetRole[0] = 'a';
    userData.remote.repeatTimes = 1;

    GPIOB_ModeCfg(GPIO_Pin_22, GPIO_ModeIN_PU);
#endif

    Main_Circulation();
}

/******************************** endfile @ main ******************************/



您好,一般是建议跑mesh的低功耗节点+长供电的朋友节点方案,可以按下方博客修改。

蓝牙mesh组网实践(手机配网例程改低功耗) - JayWell - 博客园 (cnblogs.com)

如果想用按键控制唤醒,可以参考下方博客:

在TMOS系统中手动管理休眠 - JayWell - 博客园 (cnblogs.com)

按键唤醒,是一定要用到中断的;手动管理休眠时,针对mesh组网,还要求至少每隔24h,启用一次维持10s的接受扫描,来保持IV值同步。


谢谢回复,我使用low power sleep 方法尝试手动管理电源状态,代码如下

Screenshot 2023-12-08 144530.png实际使用时发现GPIO中断唤醒后,打印了wake up from sleep,主循环也进入了一次,显示是发送了数据(实际没有发送成功,可能是因为刚唤醒还未加入mesh网络),之后GPIO中断可以正常执行,但是主循环似乎退出了,我尝试在这行printf后手动运行主循环,但是消息也无法成功发送,似乎未能加入mesh网络,尝试了在唤醒后运行main函数中的所有初始化代码均无法使用,而且会报错-7 unable to send message,似乎是协议栈底层未能正确发送数据,导致上层消息队列已满,请问这个该如何解决呢?非常感谢


您好,唤醒之后发包,需要先校准RF。

在TMOS系统中手动管理休眠 - JayWell - 博客园 (cnblogs.com)

报错-7,一般是由于发包频次高。可以调大app_mesh_config.h中的CONFIG_MESH_ADV_BUF_COUNT_DEF,或者减小send接口函数的发送参数param中的发送次数trans_cnt,或者等待底层发送完成后再发下一则消息,都可以减小发包压力。

可以在一段时间内停止发包,查看是否会出现报错-7。理论上发包缓存发送完毕就会恢复发包。如果一直报错-7,可以联系我司技术支持025-8969-2370

蓝牙mesh组网实践(常见调试问题整理) - JayWell - 博客园 (cnblogs.com)


只有登录才能回复,可以选择微信账号登录